From 79f531d82c2dd1bf6d8d53ed387936757c52c532 Mon Sep 17 00:00:00 2001 From: Artem Sheremet Date: Sun, 18 May 2025 13:01:10 +0200 Subject: [PATCH] Snapshot cc1101 component --- blinds.yaml | 3 - components/cc1101/README.md | 2 + components/cc1101/__init__.py | 406 +++++++++ components/cc1101/cc1101.cpp | 1194 ++++++++++++++++++++++++++ components/cc1101/cc1101.h | 125 +++ components/cc1101/cc1101defs.h | 693 +++++++++++++++ components/cc1101/cc1101pa.h | 684 +++++++++++++++ components/cc1101/cc1101sub.h | 57 ++ components/cc1101/number/__init__.py | 204 +++++ components/cc1101/number/number.h | 51 ++ components/cc1101/select/__init__.py | 178 ++++ components/cc1101/select/select.h | 34 + components/cc1101/switch/__init__.py | 78 ++ components/cc1101/switch/switch.h | 22 + components/cc1101/text/__init__.py | 133 +++ components/cc1101/text/text.h | 18 + 16 files changed, 3879 insertions(+), 3 deletions(-) create mode 100644 components/cc1101/README.md create mode 100644 components/cc1101/__init__.py create mode 100644 components/cc1101/cc1101.cpp create mode 100644 components/cc1101/cc1101.h create mode 100644 components/cc1101/cc1101defs.h create mode 100644 components/cc1101/cc1101pa.h create mode 100644 components/cc1101/cc1101sub.h create mode 100644 components/cc1101/number/__init__.py create mode 100644 components/cc1101/number/number.h create mode 100644 components/cc1101/select/__init__.py create mode 100644 components/cc1101/select/select.h create mode 100644 components/cc1101/switch/__init__.py create mode 100644 components/cc1101/switch/switch.h create mode 100644 components/cc1101/text/__init__.py create mode 100644 components/cc1101/text/text.h diff --git a/blinds.yaml b/blinds.yaml index 9abd428..04b2231 100644 --- a/blinds.yaml +++ b/blinds.yaml @@ -22,9 +22,6 @@ packages: external_components: # Path is relative to the main YAML file, not this device_base template! - source: components - - source: github://pr#6300 - components: - - cc1101 spi: clk_pin: 14 diff --git a/components/cc1101/README.md b/components/cc1101/README.md new file mode 100644 index 0000000..319e76d --- /dev/null +++ b/components/cc1101/README.md @@ -0,0 +1,2 @@ +This is a snapshot of https://github.com/esphome/esphome/pull/6300 that is +confirmed to work with our hardware. diff --git a/components/cc1101/__init__.py b/components/cc1101/__init__.py new file mode 100644 index 0000000..f832e64 --- /dev/null +++ b/components/cc1101/__init__.py @@ -0,0 +1,406 @@ +from esphome import automation, pins +from esphome.automation import maybe_simple_id +import esphome.codegen as cg +from esphome.components import ( + binary_sensor, + remote_base, + sensor, + spi, + text_sensor, + voltage_sampler, +) +import esphome.config_validation as cv +from esphome.const import ( + CONF_CHANNEL, + CONF_CODE, + CONF_FREQUENCY, + CONF_ID, + CONF_PROTOCOL, + CONF_SENSOR, + CONF_TEMPERATURE, + CONF_WAIT_TIME, + # DEVICE_CLASS_FREQUENCY, + DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TEMPERATURE, + ENTITY_CATEGORY_DIAGNOSTIC, + ICON_CHIP, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_DECIBEL_MILLIWATT, + UNIT_EMPTY, +) + +CODEOWNERS = ["@gabest11"] +DEPENDENCIES = ["spi"] +AUTO_LOAD = [ + "remote_base", + "voltage_sampler", + "sensor", + "binary_sensor", + "text_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 = "dBuV" + +ns = cg.esphome_ns.namespace("cc1101") +CC1101Component = ns.class_("CC1101Component", cg.PollingComponent, spi.SPIDevice) + +CONF_CC1101_ID = "cc1101_id" + +CONF_GDO0_PIN = "gdo0_pin" +CONF_GDO0_ADC_ID = "gdo0_adc_id" +CONF_OUTPUT_POWER = "output_power" +CONF_RX_ATTENUATION = "rx_attenuation" +CONF_DC_BLOCKING_FILTER = "dc_blocking_filter" + +# tuner +CONF_TUNER = "tuner" +# CONF_FREQUENCY = "frequency" +CONF_IF_FREQUENCY = "if_frequency" +CONF_BANDWIDTH = "bandwidth" +# CONF_CHANNEL = "channel" +CONF_CHANNEL_SPACING = "channel_spacing" +CONF_FSK_DEVIATION = "fsk_deviation" +CONF_MSK_DEVIATION = "msk_deviation" +CONF_SYMBOL_RATE = "symbol_rate" +CONF_SYNC_MODE = "sync_mode" +CONF_CARRIER_SENSE_ABOVE_THRESHOLD = "carrier_sense_above_threshold" +CONF_MODULATION = "modulation" + +# agc +CONF_AGC = "agc" +CONF_MAGN_TARGET = "magn_target" +CONF_MAX_LNA_GAIN = "max_lna_gain" +CONF_MAX_DVGA_GAIN = "max_dvga_gain" +CONF_CARRIER_SENSE_ABS_THR = "carrier_sense_abs_thr" +CONF_CARRIER_SENSE_REL_THR = "carrier_sense_rel_thr" +CONF_LNA_PRIORITY = "lna_priority" +CONF_FILTER_LENGTH_FSK_MSK = "filter_length_fsk_msk" +CONF_FILTER_LENGTH_ASK_OOK = "filter_length_ask_ook" +CONF_FREEZE = "freeze" +CONF_HYST_LEVEL = "hyst_level" + +# sensor +CONF_CHIP_ID = "chip_id" +CONF_RSSI = "rssi" +CONF_LQI = "lqi" +# CONF_TEMPERATURE = "temperature" + +SyncMode = ns.enum("SyncMode", True) +SYNC_MODE = { + "None": SyncMode.SYNC_MODE_NONE, + "15/16": SyncMode.SYNC_MODE_15_16, + "16/16": SyncMode.SYNC_MODE_16_16, + "30/32": SyncMode.SYNC_MODE_30_32, +} + +Modulation = ns.enum("Modulation", True) +MODULATION = { + "2-FSK": Modulation.MODULATION_2_FSK, + "GFSK": Modulation.MODULATION_GFSK, + "UNUSED 2": Modulation.MODULATION_UNUSED_2, + "ASK/OOK": Modulation.MODULATION_ASK_OOK, + "4-FSK": Modulation.MODULATION_4_FSK, + "UNUSED 5": Modulation.MODULATION_UNUSED_5, + "UNUSED 6": Modulation.MODULATION_UNUSED_6, + "MSK": Modulation.MODULATION_MSK, +} + +RxAttenuation = ns.enum("RxAttenuation", True) +RX_ATTENUATION = { + "0dB": RxAttenuation.RX_ATTENUATION_0DB, + "6dB": RxAttenuation.RX_ATTENUATION_6DB, + "12dB": RxAttenuation.RX_ATTENUATION_12DB, + "18dB": RxAttenuation.RX_ATTENUATION_18DB, +} + +MagnTarget = ns.enum("MagnTarget", True) +MAGN_TARGET = { + "24dB": MagnTarget.MAGN_TARGET_24DB, + "27dB": MagnTarget.MAGN_TARGET_27DB, + "30dB": MagnTarget.MAGN_TARGET_30DB, + "33dB": MagnTarget.MAGN_TARGET_33DB, + "36dB": MagnTarget.MAGN_TARGET_36DB, + "38dB": MagnTarget.MAGN_TARGET_38DB, + "40dB": MagnTarget.MAGN_TARGET_40DB, + "42dB": MagnTarget.MAGN_TARGET_42DB, +} + +MaxLnaGain = ns.enum("MaxLnaGain", True) +MAX_LNA_GAIN = { + "Default": MaxLnaGain.MAX_LNA_GAIN_DEFAULT, + "2.6dB": MaxLnaGain.MAX_LNA_GAIN_MINUS_2P6DB, + "6.1dB": MaxLnaGain.MAX_LNA_GAIN_MINUS_6P1DB, + "7.4dB": MaxLnaGain.MAX_LNA_GAIN_MINUS_7P4DB, + "9.2dB": MaxLnaGain.MAX_LNA_GAIN_MINUS_9P2DB, + "11.5dB": MaxLnaGain.MAX_LNA_GAIN_MINUS_11P5DB, + "14.6dB": MaxLnaGain.MAX_LNA_GAIN_MINUS_14P6DB, + "17.1dB": MaxLnaGain.MAX_LNA_GAIN_MINUS_17P1DB, +} + +MaxDvgaGain = ns.enum("MaxDvgaGain", True) +MAX_DVGA_GAIN = { + "Default": MaxDvgaGain.MAX_DVGA_GAIN_DEFAULT, + "-1": MaxDvgaGain.MAX_DVGA_GAIN_MINUS_1, + "-2": MaxDvgaGain.MAX_DVGA_GAIN_MINUS_2, + "-3": MaxDvgaGain.MAX_DVGA_GAIN_MINUS_3, +} + +CarrierSenseRelThr = ns.enum("CarrierSenseRelThr", True) +CARRIER_SENSE_REL_THR = { + "Default": CarrierSenseRelThr.CARRIER_SENSE_REL_THR_DEFAULT, + "+6dB": CarrierSenseRelThr.CARRIER_SENSE_REL_THR_PLUS_6DB, + "+10dB": CarrierSenseRelThr.CARRIER_SENSE_REL_THR_PLUS_10DB, + "+14dB": CarrierSenseRelThr.CARRIER_SENSE_REL_THR_PLUS_14DB, +} + +FilterLengthFskMsk = ns.enum("FilterLengthFskMsk", True) +FILTER_LENGTH_FSK_MSK = { + "8": FilterLengthFskMsk.FILTER_LENGTH_8, + "16": FilterLengthFskMsk.FILTER_LENGTH_16, + "32": FilterLengthFskMsk.FILTER_LENGTH_32, + "64": FilterLengthFskMsk.FILTER_LENGTH_64, +} + +FilterLengthAskOok = ns.enum("FilterLengthAskOok", True) +FILTER_LENGTH_ASK_OOK = { + "4dB": FilterLengthAskOok.FILTER_LENGTH_4DB, + "8dB": FilterLengthAskOok.FILTER_LENGTH_8DB, + "12dB": FilterLengthAskOok.FILTER_LENGTH_12DB, + "16dB": FilterLengthAskOok.FILTER_LENGTH_16DB, +} + +Freeze = ns.enum("Freeze", True) +FREEZE = { + "Default": Freeze.AGC_FREEZE_DEFAULT, + "On Sync": Freeze.AGC_FREEZE_ON_SYNC, + "Analog Only": Freeze.AGC_FREEZE_ANALOG_ONLY, + "Analog And Digital": Freeze.AGC_FREEZE_ANALOG_AND_DIGITAL, +} + +WaitTime = ns.enum("WaitTime", True) +WAIT_TIME = { + "8": WaitTime.WAIT_TIME_8_SAMPLES, + "16": WaitTime.WAIT_TIME_16_SAMPLES, + "24": WaitTime.WAIT_TIME_24_SAMPLES, + "32": WaitTime.WAIT_TIME_32_SAMPLES, +} + +HystLevel = ns.enum("HystLevel", True) +HYST_LEVEL = { + "None": HystLevel.HYST_LEVEL_NONE, + "Low": HystLevel.HYST_LEVEL_LOW, + "Medium": HystLevel.HYST_LEVEL_MEDIUM, + "High": HystLevel.HYST_LEVEL_HIGH, +} + +TYPES = { + None: { + CONF_OUTPUT_POWER: [cv.float_range(-70, 11)], + CONF_RX_ATTENUATION: [cv.enum(RX_ATTENUATION)], + CONF_DC_BLOCKING_FILTER: [cv.boolean], + }, + CONF_TUNER: { + CONF_FREQUENCY: [cv.float_range(300000, 928000)], + CONF_IF_FREQUENCY: [cv.float_range(25, 788)], + CONF_BANDWIDTH: [cv.float_range(58, 812)], + CONF_CHANNEL: [cv.uint8_t], + CONF_CHANNEL_SPACING: [cv.float_range(25, 405)], + CONF_FSK_DEVIATION: [cv.float_range(1.5, 381)], + CONF_MSK_DEVIATION: [cv.int_range(1, 8)], + CONF_SYMBOL_RATE: [cv.float_range(600, 500000)], + CONF_SYNC_MODE: [cv.enum(SYNC_MODE)], + CONF_CARRIER_SENSE_ABOVE_THRESHOLD: [cv.boolean], + CONF_MODULATION: [cv.enum(MODULATION)], + }, + CONF_AGC: { + CONF_MAGN_TARGET: [cv.enum(MAGN_TARGET)], + CONF_MAX_LNA_GAIN: [cv.enum(MAX_LNA_GAIN)], + CONF_MAX_DVGA_GAIN: [cv.enum(MAX_DVGA_GAIN)], + CONF_CARRIER_SENSE_ABS_THR: [cv.int_range(-8, 7)], + CONF_CARRIER_SENSE_REL_THR: [cv.enum(CARRIER_SENSE_REL_THR)], + CONF_LNA_PRIORITY: [cv.boolean], + CONF_FILTER_LENGTH_FSK_MSK: [cv.enum(FILTER_LENGTH_FSK_MSK)], + CONF_FILTER_LENGTH_ASK_OOK: [cv.enum(FILTER_LENGTH_ASK_OOK)], + CONF_FREEZE: [cv.enum(FREEZE)], + CONF_WAIT_TIME: [cv.enum(WAIT_TIME)], + CONF_HYST_LEVEL: [cv.enum(HYST_LEVEL)], + }, + CONF_SENSOR: { + CONF_CHIP_ID: [ + text_sensor.text_sensor_schema( + # unit_of_measurement=UNIT_, + # accuracy_decimals=3, + # device_class=DEVICE_CLASS_, + # state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + icon=ICON_CHIP, + ), + "text_sensor", + ], + CONF_RSSI: [ + sensor.sensor_schema( + unit_of_measurement=UNIT_DECIBEL_MILLIWATT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_SIGNAL_STRENGTH, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + # icon=ICON_CHIP, + ), + "sensor", + ], + CONF_LQI: [ + sensor.sensor_schema( + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + # device_class=DEVICE_CLASS_SIGNAL_STRENGTH, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + # icon=ICON_CHIP, + ), + "sensor", + ], + CONF_TEMPERATURE: [ + sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + # icon=ICON_CHIP, + ), + "sensor", + ], + }, +} + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CC1101Component), + cv.Optional(CONF_GDO0_PIN): cv.All(pins.internal_gpio_output_pin_schema), + cv.Optional(CONF_GDO0_ADC_ID): cv.use_id(voltage_sampler.VoltageSampler), + } + ) + .extend( + {cv.Optional(k): v[0] for k, v in TYPES[None].items()}, + { + cv.Optional(CONF_TUNER): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_TUNER].items()} + ), + cv.Optional(CONF_AGC): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_AGC].items()} + ), + cv.Optional(CONF_SENSOR): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_SENSOR].items()} + ), + }, + ) + .extend(cv.polling_component_schema("60s")) + .extend(spi.spi_device_schema(cs_pin_required=True)) +) + +CC1101_COMPONENT_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_CC1101_ID): cv.use_id(CC1101Component), + } +) + + +async def for_each_conf(config, types, callback): + for section in types: + if section is not None and section not in config: + continue + c = config[section] if section is not None else config + for type, args in types[section].items(): + if type not in c: + continue + setter = "set_" + if section is not None and section != CONF_SENSOR: + setter += section + "_" + setter += type + # print(setter + "(" + repr(c[type]) + ")") + await callback(c[type], args[1:], setter) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await spi.register_spi_device(var, config) + if CONF_GDO0_PIN in config: + gdo0_pin = await cg.gpio_pin_expression(config[CONF_GDO0_PIN]) + cg.add(var.set_config_gdo0_pin(gdo0_pin)) + if CONF_GDO0_ADC_ID in config: + gdo0_adc_id = await cg.get_variable(config[CONF_GDO0_ADC_ID]) + cg.add(var.set_config_gdo0_adc_pin(gdo0_adc_id)) + + async def set_var(c, args, setter): + if len(args) > 0: + s = None + if args[0] == "sensor": + s = await sensor.new_sensor(c) + elif args[0] == "binary_sensor": + s = await binary_sensor.new_binary_sensor(c) + elif args[0] == "text_sensor": + s = await text_sensor.new_text_sensor(c) + if s is not None: + cg.add(getattr(var, setter + "_" + args[0])(s)) + else: + cg.add(getattr(var, setter)(c)) + + await for_each_conf(config, TYPES, set_var) + + +BeginTxAction = ns.class_("BeginTxAction", automation.Action) +EndTxAction = ns.class_("EndTxAction", automation.Action) + +CC1101_ACTION_SCHEMA = maybe_simple_id( + { + cv.GenerateID(CONF_ID): cv.use_id(CC1101Component), + } +) + + +@automation.register_action("cc1101.begin_tx", BeginTxAction, CC1101_ACTION_SCHEMA) +@automation.register_action("cc1101.end_tx", EndTxAction, CC1101_ACTION_SCHEMA) +async def cc1101_action_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 + + +CC1101RawAction = ns.class_("CC1101RawAction", remote_base.RCSwitchRawAction) + +CC1101_TRANSMIT_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(CONF_CC1101_ID): cv.use_id(CC1101Component), + } + ) + .extend(remote_base.REMOTE_TRANSMITTABLE_SCHEMA) + .extend(remote_base.RC_SWITCH_RAW_SCHEMA) + .extend(remote_base.RC_SWITCH_TRANSMITTER) +) + + +@remote_base.register_action( + "rc_switch_raw_cc1101", CC1101RawAction, CC1101_TRANSMIT_SCHEMA +) +async def rc_switch_raw_cc1101_action(var, config, args): + proto = await cg.templatable( + config[CONF_PROTOCOL], + args, + remote_base.RCSwitchBase, + to_exp=remote_base.build_rc_switch_protocol, + ) + cg.add(var.set_protocol(proto)) + cg.add(var.set_code(await cg.templatable(config[CONF_CODE], args, cg.std_string))) + await cg.register_parented(var, config[CONF_CC1101_ID]) diff --git a/components/cc1101/cc1101.cpp b/components/cc1101/cc1101.cpp new file mode 100644 index 0000000..283567f --- /dev/null +++ b/components/cc1101/cc1101.cpp @@ -0,0 +1,1194 @@ +#include "cc1101.h" +#include "cc1101pa.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include +#include +#include + +namespace esphome { +namespace cc1101 { + +static const char *const TAG = "cc1101"; + +CC1101Component::CC1101Component() { + this->gdo0_ = nullptr; + this->gdo0_adc_ = nullptr; + this->reset_ = false; + this->output_power_requested_ = OUTPUT_POWER_MAX; + this->output_power_effective_ = OUTPUT_POWER_MAX; + memset(this->pa_table_, 0, sizeof(pa_table_)); + memset(&this->state_, 0, sizeof(this->state_)); + + // datasheet defaults (non-listed fields are zero) + + this->state_.GDO2_CFG = 0x29; + this->state_.GDO1_CFG = 0x2E; + this->state_.GDO0_CFG = 0x3F; + this->state_.FIFO_THR = 7; + this->state_.SYNC1 = 0xD3; + this->state_.SYNC0 = 0x91; + this->state_.PKTLEN = 0xFF; + this->state_.APPEND_STATUS = 1; + this->state_.LENGTH_CONFIG = 1; + this->state_.CRC_EN = 1; + this->state_.WHITE_DATA = 1; + this->state_.FREQ_IF = 0x0F; + this->state_.FREQ2 = 0x1E; + this->state_.FREQ1 = 0xC4; + this->state_.FREQ0 = 0xEC; + this->state_.DRATE_E = 0x0C; + this->state_.CHANBW_E = 0x02; + this->state_.DRATE_M = 0x22; + this->state_.SYNC_MODE = 2; + this->state_.CHANSPC_E = 2; + this->state_.NUM_PREAMBLE = 2; + this->state_.CHANSPC_M = 0xF8; + this->state_.DEVIATION_M = 7; + this->state_.DEVIATION_E = 4; + this->state_.RX_TIME = 7; + this->state_.CCA_MODE = 3; + this->state_.PO_TIMEOUT = 1; + this->state_.FOC_LIMIT = 2; + this->state_.FOC_POST_K = 1; + this->state_.FOC_PRE_K = 2; + this->state_.FOC_BS_CS_GATE = 1; + this->state_.BS_POST_KP = 1; + this->state_.BS_POST_KI = 1; + this->state_.BS_PRE_KP = 2; + this->state_.BS_PRE_KI = 1; + this->state_.MAGN_TARGET = 3; + this->state_.AGC_LNA_PRIORITY = 1; + this->state_.FILTER_LENGTH = 1; + this->state_.WAIT_TIME = 1; + this->state_.HYST_LEVEL = 2; + this->state_.WOREVT1 = 0x87; + this->state_.WOREVT0 = 0x6B; + this->state_.RC_CAL = 1; + this->state_.EVENT1 = 7; + this->state_.RC_PD = 1; + this->state_.MIX_CURRENT = 2; + this->state_.LODIV_BUF_CURRENT_RX = 1; + this->state_.LNA2MIX_CURRENT = 1; + this->state_.LNA_CURRENT = 1; + this->state_.LODIV_BUF_CURRENT_TX = 1; + this->state_.FSCAL3_LO = 9; + this->state_.CHP_CURR_CAL_EN = 2; + this->state_.FSCAL3_HI = 2; + this->state_.FSCAL2 = 0x0A; + this->state_.FSCAL1 = 0x20; + this->state_.FSCAL0 = 0x0D; + this->state_.RCCTRL1 = 0x41; + this->state_.FSTEST = 0x59; + this->state_.PTEST = 0x7F; + this->state_.AGCTEST = 0x3F; + this->state_.TEST2 = 0x88; + this->state_.TEST1 = 0x31; + this->state_.TEST0_LO = 1; + this->state_.VCO_SEL_CAL_EN = 1; + this->state_.TEST0_HI = 2; + + // old settings which make no sense to me + + /* + // MDMCFG1 + this->state_.NUM_PREAMBLE = 0; // ? + this->state_.PO_TIMEOUT = 2; // Approx. 149 – 155 μs + // FOCCFG + this->state_.FOC_BS_CS_GATE = 0; // ? + // BSCFG + this->state_.BS_PRE_KP = 1; // ? + // FSCAL3 + this->state_.FSCAL3_HI = 3; // ? + // FSCAL2 + this->state_.VCO_CORE_H_EN = 1; // Choose high (1) / low (0) VCO ? + // FSCAL1 + this->state_.FSCAL1 = 0; // ? + // FSCAL0 + this->state_.FSCAL0 = 31; // ? + // TEST0 + this->state_.VCO_SEL_CAL_EN = 0; // ? + */ + + // old settings that kinda make sense + + // TODO: this->set_mode_(false) + + // IOCFGx + this->state_.GDO2_CFG = 0x0D; // Async serial output (TODO: enum) + this->state_.GDO0_CFG = 0x0D; + + // PKTCTRL0 + // Setting PKTCTRL0.PKT_FORMAT to 3 enables asynchronous serial mode. In TX, the + // GDO0 pin is used for data input (TX data). Data output can be on GDO0, GDO1, or + // GDO2. This is set by the IOCFG0.GDO0_CFG, IOCFG1.GDO1_CFG + // and IOCFG2.GDO2_CFG fields. + this->state_.PKT_FORMAT = 3; // (TODO: enum) + // With PKTCTRL0.LENGTH_CONFIG=2, the packet length is set to infinite and transmission + // and reception will continue until turned off manually. As described in the next section, + // this can be used to support packet formats with different length configuration than natively + // supported by CC1101 + this->state_.LENGTH_CONFIG = 2; // (TODO: enum) + + // MDMCFG0 (TODO: this->set_..) + this->state_.FS_AUTOCAL = 1; // When going from IDLE to RX or TX (or FSTXON) + + this->set_tuner_frequency(433920); + this->set_tuner_if_frequency(153); + this->set_tuner_bandwidth(203); + this->set_tuner_channel(0); + this->set_tuner_channel_spacing(200); + this->set_tuner_symbol_rate(5000); + this->set_tuner_sync_mode(SyncMode::SYNC_MODE_NONE); + this->set_tuner_carrier_sense_above_threshold(true); + this->set_tuner_modulation(Modulation::MODULATION_ASK_OOK); + // this->set_tuner_fsk_deviation(47.607f); + // this->set_tuner_msk_deviation(0); + this->set_agc_magn_target(MagnTarget::MAGN_TARGET_42DB); // ? + this->set_agc_max_lna_gain(MaxLnaGain::MAX_LNA_GAIN_DEFAULT); + this->set_agc_max_dvga_gain(MaxDvgaGain::MAX_DVGA_GAIN_MINUS_3); // ? + this->set_agc_lna_priority(false); // ? + this->set_agc_wait_time(WaitTime::WAIT_TIME_32_SAMPLES); // ? +} + +// overrides + +void CC1101Component::setup() { + if (this->gdo0_ != nullptr) { +#ifdef USE_ESP8266 + // ESP8266 GDO0 generally input, switched to output for TX + // ESP32 GDO0 output, GDO2 input + // if there is an ADC, GDO0 is input only while reading temperature + this->gdo0_->setup(); + this->gdo0_->pin_mode(gpio::FLAG_INPUT); +#endif + } + + // datasheet 19.1.2 + + this->cs_->digital_write(true); + delayMicroseconds(1); + this->cs_->digital_write(false); + delayMicroseconds(1); + this->cs_->digital_write(true); + delayMicroseconds(41); + this->cs_->digital_write(false); + delayMicroseconds(5000); + + this->spi_setup(); + + // reset + + this->send_(Command::RES); + + // Read part number and version + + this->read_(Register::PARTNUM); + this->read_(Register::VERSION); + + if (this->state_.VERSION == 0) { + mark_failed(); + ESP_LOGE(TAG, "Failed to reset CC1101 modem. Check connection."); + return; + } + + char buff[32] = {0}; + snprintf(buff, sizeof(buff), "%02X%02X", this->state_.PARTNUM, this->state_.VERSION); + this->chip_id_ = buff; + + ESP_LOGD(TAG, "%s was found", this->chip_id_.c_str()); + + for (uint8_t i = 0; i <= 0x2E; i++) { + this->write_((Register) i); + } + + this->write_(Register::PATABLE, this->pa_table_, sizeof(this->pa_table_)); + + ESP_LOGV(TAG, "verify"); + + for (uint8_t i = 0; i <= 0x2E; i++) { + uint8_t old_value = this->regs_[i]; + this->read_((Register) i); + ESP_LOGV(TAG, "[%d] %02X vs %02X%s", i, old_value, this->regs_[i], old_value != this->regs_[i] ? " ***" : ""); + } + + this->send_(Command::RX); + + this->publish_output_power(); + this->publish_rx_attenuation(); + this->publish_dc_blocking_filter(); + this->publish_tuner_frequency(); + this->publish_tuner_if_frequency(); + this->publish_tuner_bandwidth(); + this->publish_tuner_channel(); + this->publish_tuner_channel_spacing(); + this->publish_tuner_fsk_deviation(); + this->publish_tuner_msk_deviation(); + this->publish_tuner_symbol_rate(); + this->publish_tuner_sync_mode(); + this->publish_tuner_carrier_sense_above_threshold(); + this->publish_tuner_modulation(); + this->publish_agc_magn_target(); + this->publish_agc_max_lna_gain(); + this->publish_agc_max_dvga_gain(); + this->publish_agc_carrier_sense_abs_thr(); + this->publish_agc_carrier_sense_rel_thr(); + this->publish_agc_lna_priority(); + this->publish_agc_filter_length_fsk_msk(); + this->publish_agc_filter_length_ask_ook(); + this->publish_agc_freeze(); + this->publish_agc_wait_time(); + this->publish_agc_hyst_level(); + + this->publish_chip_id_text_sensor(); + this->publish_rssi_sensor(); + this->publish_lqi_sensor(); + this->publish_temperature_sensor(); +} + +void CC1101Component::dump_config() { + ESP_LOGCONFIG(TAG, "CC1101: "); + // LOG_SPI_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "failed!"); + } + ESP_LOGCONFIG(TAG, " Chip: %s", this->chip_id_.c_str()); + ESP_LOGCONFIG(TAG, " Tuner:"); + ESP_LOGCONFIG(TAG, " Output Power: %.1f dBm", this->get_output_power()); + ESP_LOGCONFIG(TAG, " Rx Attenuation: %d dB", (int) this->get_rx_attenuation() * 6); + ESP_LOGCONFIG(TAG, " DC Blocking Filter: %d", (int) this->get_dc_blocking_filter()); + ESP_LOGCONFIG(TAG, " Frequency: %d KHz", (int) this->get_tuner_frequency()); + ESP_LOGCONFIG(TAG, " IF Frequency: %d KHz", (int) this->get_tuner_if_frequency()); + ESP_LOGCONFIG(TAG, " Bandwith: %d KHz", (int) this->get_tuner_bandwidth()); + ESP_LOGCONFIG(TAG, " Channel: %d", (int) this->get_tuner_channel()); + ESP_LOGCONFIG(TAG, " Channel Spacing: %d KHz", (int) this->get_tuner_channel_spacing()); + ESP_LOGCONFIG(TAG, " FSK Deviation: %d KHz", (int) this->get_tuner_fsk_deviation()); + ESP_LOGCONFIG(TAG, " MSK Deviation: %d KHz", (int) this->get_tuner_msk_deviation()); + ESP_LOGCONFIG(TAG, " Symbol Rate: %d Baud", (int) this->get_tuner_symbol_rate()); + ESP_LOGCONFIG(TAG, " Sync Mode: %d", (int) this->get_tuner_sync_mode()); + ESP_LOGCONFIG(TAG, " Carrier Sense Above Threshold: %d", (int) this->get_tuner_carrier_sense_above_threshold()); + ESP_LOGCONFIG(TAG, " Modulation: %d", (int) this->get_tuner_modulation()); + // TODO: ...and everything else... + LOG_PIN(" CS Pin: ", this->cs_); + LOG_PIN(" GDO0: ", this->gdo0_); + LOG_TEXT_SENSOR(" ", "Chip ID", this->chip_id_text_sensor_); + LOG_SENSOR(" ", "RSSI", this->rssi_sensor_); + LOG_SENSOR(" ", "LQI", this->lqi_sensor_); + LOG_SENSOR(" ", "Temperature sensor", this->temperature_sensor_); + LOG_UPDATE_INTERVAL(this); +} + +void CC1101Component::update() { + this->read_(Register::RSSI); + this->publish_rssi_sensor(); + + this->read_(Register::LQI); + this->publish_lqi_sensor(); + + if (this->gdo0_ != nullptr && this->gdo0_adc_ != nullptr) { + // TODO: read temperature if possible + this->publish_temperature_sensor(); + } +} + +void CC1101Component::loop() {} + +// actions + +void CC1101Component::begin_tx() { + this->send_(Command::TX); + + if (this->gdo0_ != nullptr) { +#ifdef USE_ESP8266 +#ifdef USE_ARDUINO + noInterrupts(); // NOLINT +#else // USE_ESP_IDF + portDISABLE_INTERRUPTS() +#endif + this->gdo0_->pin_mode(gpio::FLAG_OUTPUT); +#endif + } +} + +void CC1101Component::end_tx() { + if (this->gdo0_ != nullptr) { +#ifdef USE_ESP8266 +#ifdef USE_ARDUINO + interrupts(); // NOLINT +#else // USE_ESP_IDF + portENABLE_INTERRUPTS() +#endif + this->gdo0_->pin_mode(gpio::FLAG_INPUT); +#endif + } + + this->send_(Command::RX); +} + +// protected + +void CC1101Component::strobe_(Command cmd) { + uint8_t index = (uint8_t) cmd; + if (cmd < Command::RES || cmd > Command::NOP) { + ESP_LOGE(TAG, "%s(0x%02X) invalid register address", __func__, index); + return; + } + + if (!this->reset_) { + if (this->get_component_state() & COMPONENT_STATE_LOOP) { + ESP_LOGE(TAG, "%s(0x%02X) device was not reset", __func__, index); + return; + } + } + + this->enable(); + this->write_byte(index); + this->disable(); + + ESP_LOGV(TAG, "%s(0x%02X)", __func__, index); +} + +void CC1101Component::write_(Register reg) { + uint8_t index = (uint8_t) reg; + if (reg > Register::TEST0 || reg == Register::FSTEST || reg == Register::AGCTEST) { + ESP_LOGE(TAG, "%s(0x%02X) invalid register address", __func__, index); + return; + } + + if (!this->reset_) { + if (this->get_component_state() & COMPONENT_STATE_LOOP) { + ESP_LOGE(TAG, "%s(0x%02X) device was not reset", __func__, index); + return; + } + } + + uint8_t value = this->regs_[index]; + + this->enable(); + this->write_byte(index); + this->transfer_array(&value, 1); + this->disable(); + + ESP_LOGV(TAG, "%s(0x%02X) = 0x%02X", __func__, index, this->regs_[index]); +} + +void CC1101Component::write_(Register reg, uint8_t value) { + uint8_t index = (uint8_t) reg; + if (reg > Register::TEST0 || reg == Register::FSTEST || reg == Register::AGCTEST) { + ESP_LOGE(TAG, "%s(0x%02X) invalid register address", __func__, index); + return; + } + + this->regs_[index] = value; + this->write_(reg); +} + +void CC1101Component::write_(Register reg, uint8_t *buffer, size_t length) { + uint8_t index = (uint8_t) reg; + if (reg != Register::PATABLE && reg != Register::FIFO) { + ESP_LOGE(TAG, "%s(0x%02X) invalid register address", __func__, index); + return; + } + + if (!this->reset_) { + if (this->get_component_state() & COMPONENT_STATE_LOOP) { + ESP_LOGE(TAG, "%s(0x%02X) device was not reset", __func__, index); + return; + } + } + + this->enable(); + this->write_byte(index | BUS_WRITE | BUS_BURST); + this->transfer_array(buffer, length); + this->disable(); + + ESP_LOGV(TAG, "%s(0x%02X) %zu", __func__, index, length); +} + +bool CC1101Component::read_(Register reg) { + uint8_t index = (uint8_t) reg; + if (reg > Register::RCCTRL0_STATUS) { + ESP_LOGE(TAG, "%s(0x%02X) invalid register address", __func__, index); + return false; + } + + this->enable(); + this->write_byte(index | BUS_READ | BUS_BURST); + this->regs_[index] = this->transfer_byte(0); + this->disable(); + + return true; +} + +bool CC1101Component::read_(Register reg, uint8_t *buffer, size_t length) { + uint8_t index = (uint8_t) reg; + if (reg != Register::PATABLE && reg != Register::FIFO) { + ESP_LOGE(TAG, "%s(0x%02X) invalid register address", __func__, index); + return false; + } + + this->enable(); + this->write_byte(index | BUS_READ | BUS_BURST); + this->read_array(buffer, length); + this->disable(); + + return true; +} + +void CC1101Component::send_(Command cmd) { + if (cmd == Command::TX || cmd == Command::RX || cmd == Command::PWD) { + this->send_(Command::IDLE); + } + + ESP_LOGV(TAG, "%s(0x%02X)", __func__, (uint8_t) cmd); + + this->strobe_(cmd); + this->wait_(cmd); + + if (cmd == Command::RES) { + this->reset_ = true; + } +} + +bool CC1101Component::wait_(Command cmd) { + uint32_t start = millis(); + while ((millis() - start) < 1000) { + this->read_(Register::MARCSTATE); + State s = (State) this->state_.MARC_STATE; + if (cmd == Command::IDLE || cmd == Command::RES) { + if (s == State::IDLE) + return true; + } else if (cmd == Command::RX) { + if (s == State::RX || s == State::RX_END || s == State::RXTX_SWITCH) + return true; + } else if (cmd == Command::TX) { + if (s == State::TX || s == State::TX_END || s == State::TXRX_SWITCH) + return true; + } else { + return true; // else if TODO + } + delayMicroseconds(1); + } + mark_failed(); + ESP_LOGE(TAG, "CC1101 modem wait state timeout. Check connection."); + return false; +} + +// config + +template T GET_ENUM_LAST(T value) { return T::LAST; } + +#define CHECK_ENUM(value) \ + if ((value) >= GET_ENUM_LAST(value)) { \ + ESP_LOGE(TAG, "%s(%d) invalid", __func__, (int) (value)); \ + return; \ + } + +#define CHECK_FLOAT_RANGE(value, min_value, max_value) \ + if ((value) < (min_value) || (value) > (max_value)) { \ + ESP_LOGE(TAG, "%s(%.2f) invalid (%.2f - %.2f)", __func__, value, min_value, max_value); \ + return; \ + } + +#define CHECK_INT_RANGE(value, min_value, max_value) \ + if ((value) < (min_value) || (value) > (max_value)) { \ + ESP_LOGE(TAG, "%s(%d) invalid (%d - %d)", __func__, (int) (value), (int) (min_value), (int) (max_value)); \ + return; \ + } + +#define CHECK_TEXT_RANGE(value, max_size) \ + if ((value).size() > (max_size)) { \ + ESP_LOGW(TAG, "%s(%s) trimmed (max %d characters)", __func__, (value).c_str(), max_size); \ + (value).resize(max_size); \ + } + +/* +static int mapint(int x, int in_min, int in_max, int out_min, int out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +static int32_t mapfloat(float x, float in_min, float in_max, float out_min, float out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} +*/ + +static void split_float(float value, int mbits, uint8_t &e, uint32_t &m) { + if (value < 0) { + ESP_LOGE(TAG, "split_float(%f, %d): positive values only", value, mbits); + } + + int e_tmp; + float m_tmp = std::frexp(value, &e_tmp); + + if (e_tmp <= mbits) { + ESP_LOGW(TAG, "split_float(%f, %d): exponent would be negative, set to minimum", value, mbits); + e = 0; + m = 0; + return; + } + + // frexp returns 2^e * 0.m + // but we want the original 2^e * 1.m format with the hidden bit + + e = (uint8_t) (e_tmp - mbits - 1); + m = (uint32_t) ((m_tmp * 2 - 1) * (1 << (mbits + 1)) + 1) >> 1; + + // mantissa might overflow due to rounding, even the datasheet recommends this: + // "If DRATE_M is rounded to the nearest integer and becomes 256, + // increment DRATE_E and use DRATE_M = 0." + + if (m == (1 << mbits)) { + e = e + 1; + m = 0; + } +} + +void CC1101Component::set_output_power(float value) { + CHECK_FLOAT_RANGE(value, OUTPUT_POWER_MIN, OUTPUT_POWER_MAX) + + this->output_power_requested_ = value; + + int freq = (int) this->get_tuner_frequency(); + Modulation modulation = this->get_tuner_modulation(); + uint8_t a = 0xC0; + + if (freq >= 300000 && freq <= 348000) { + a = PowerTable::find(PA_TABLE_315, sizeof(PA_TABLE_315) / sizeof(PA_TABLE_315[0]), value); + } else if (freq >= 378000 && freq <= 464000) { + a = PowerTable::find(PA_TABLE_433, sizeof(PA_TABLE_433) / sizeof(PA_TABLE_433[0]), value); + } else if (freq >= 779000 && freq < 900000) { + a = PowerTable::find(PA_TABLE_868, sizeof(PA_TABLE_868) / sizeof(PA_TABLE_868[0]), value); + } else if (freq >= 900000 && freq <= 928000) { + a = PowerTable::find(PA_TABLE_915, sizeof(PA_TABLE_915) / sizeof(PA_TABLE_915[0]), value); + } else { + ESP_LOGE(TAG, "frequency out of range: %d", freq); + } + + if (modulation == Modulation::MODULATION_ASK_OOK) { + this->pa_table_[0] = 0; + this->pa_table_[1] = a; + } else { + this->pa_table_[0] = a; + this->pa_table_[1] = 0; + } + + this->output_power_effective_ = value; + + ESP_LOGD(TAG, "set_output_power(%.1f) %d", value, a); + + this->publish_output_power(); + + if (!this->reset_) { + return; + } + + this->write_(Register::PATABLE, this->pa_table_, sizeof(this->pa_table_)); +} + +float CC1101Component::get_output_power() { return this->output_power_effective_; } + +void CC1101Component::set_rx_attenuation(RxAttenuation value) { + CHECK_ENUM(value) + + this->state_.CLOSE_IN_RX = (uint8_t) value; + + ESP_LOGD(TAG, "set_rx_attenuation(%d)", (int) value); + + this->publish_rx_attenuation(); + + if (!this->reset_) { + return; + } + + this->write_(Register::FIFOTHR); +} + +RxAttenuation CC1101Component::get_rx_attenuation() { return (RxAttenuation) this->state_.CLOSE_IN_RX; } + +void CC1101Component::set_dc_blocking_filter(bool value) { + this->state_.DEM_DCFILT_OFF = value ? 0 : 1; // inverted logic, 0 is Enable + + ESP_LOGD(TAG, "set_dc_blocking_filter(%d)", value ? 1 : 0); + + this->publish_dc_blocking_filter(); + + if (!this->reset_) { + return; + } + + this->write_(Register::MDMCFG2); +} + +bool CC1101Component::get_dc_blocking_filter() { return this->state_.DEM_DCFILT_OFF == 0; } + +// tuner_* + +void CC1101Component::set_tuner_frequency(float value) { + CHECK_FLOAT_RANGE(value, FREQUENCY_MIN, FREQUENCY_MAX) + + // strange frequencies like 360MHz also seem to work, not sure about the quality + + int freq = (int) (value * (1 << 16) / XTAL_FREQUENCY); + + this->state_.FREQ2 = (uint8_t) (freq >> 16); + this->state_.FREQ1 = (uint8_t) (freq >> 8); + this->state_.FREQ0 = (uint8_t) freq; + + this->set_output_power(this->output_power_requested_); // frequency and modulation dependent + + ESP_LOGD(TAG, "set_tuner_frequency(%f) FREQ = %02X%02X%02X", value, this->state_.FREQ2, this->state_.FREQ1, + this->state_.FREQ0); + + this->publish_tuner_frequency(); + + if (!this->reset_) { + return; + } + + // If any frequency programming register is altered when the frequency synthesizer is + // running, the synthesizer may give an undesired response. Hence, the frequency + // programming should only be updated when the radio is in the IDLE state. + + // TODO: this may also apply when setting other registers + // channel (CHANNR.CHAN) + // channel_spacing (MDMCFG1.CHANSPC_*) + // if_frequency (FSCTRL1.FREQ_IF) + + this->send_(Command::IDLE); + + this->write_(Register::FREQ2); + this->write_(Register::FREQ1); + this->write_(Register::FREQ0); + + // calibration + /* + if (freq >= 300000 && freq <= 348000) { + this->write_(Register::FSCTRL0, (uint8_t) mapint(freq, 300000, 348000, 24, 28)); + + if (freq < 322880) { + this->write_(Register::TEST0, 0x0B); + } else { + this->write_(Register::TEST0, 0x09); + this->read_(Register::FSCAL2); + if(this->state_.VCO_CORE_H_EN == 0) { + this->state_.VCO_CORE_H_EN = 1; + this->write_(Register::FSCAL2); + } + } + } else if (freq >= 378000 && freq <= 464000) { + this->write_(Register::FSCTRL0, (uint8_t) mapint(freq, 378000, 464000, 31, 38)); + + if (freq < 430500) { + this->write_(Register::TEST0, 0x0B); + } else { + this->write_(Register::TEST0, 0x09); + this->read_(Register::FSCAL2); + if(this->state_.VCO_CORE_H_EN == 0) { + this->state_.VCO_CORE_H_EN = 1; + this->write_(Register::FSCAL2); + } + } + } else if (freq >= 779000 && freq < 900000) { + this->write_(Register::FSCTRL0, (uint8_t) mapint(freq, 779000, 899000, 65, 76)); + + if (freq < 861000) { + this->write_(Register::TEST0, 0x0B); + } else { + this->write_(Register::TEST0, 0x09); + this->read_(Register::FSCAL2); + if(this->state_.VCO_CORE_H_EN == 0) { + this->state_.VCO_CORE_H_EN = 1; + this->write_(Register::FSCAL2); + } + } + } else if (freq >= 900000 && freq <= 928000) { + this->write_(Register::FSCTRL0, (uint8_t) mapint(freq, 900000, 928000, 77, 79)); + //if (freq < ) { + // this->write_(Register::TEST0, 0x0B); + //} else { + // this->write_(Register::TEST0, 0x09); + this->read_(Register::FSCAL2); + if(this->state_.VCO_CORE_H_EN == 0) { + this->state_.VCO_CORE_H_EN = 1; + this->write_(Register::FSCAL2); + } + //} + } + */ + + this->send_(Command::RX); +} + +float CC1101Component::get_tuner_frequency() { + uint32_t freq = 0; + freq |= (uint32_t) this->state_.FREQ2 << 16; + freq |= (uint32_t) this->state_.FREQ1 << 8; + freq |= (uint32_t) this->state_.FREQ0; + return roundf((float) XTAL_FREQUENCY * freq / (1 << 16)); +} + +void CC1101Component::set_tuner_if_frequency(float value) { + CHECK_FLOAT_RANGE(value, IF_FREQUENCY_MIN, IF_FREQUENCY_MAX) + + this->state_.FREQ_IF = value * (1 << 10) / XTAL_FREQUENCY; + + ESP_LOGD(TAG, "set_tuner_if_frequency(%f)", value); + + this->publish_tuner_if_frequency(); + + if (!this->reset_) { + return; + } + + this->write_(Register::FSCTRL1); +} + +float CC1101Component::get_tuner_if_frequency() { return XTAL_FREQUENCY * this->state_.FREQ_IF / (1 << 10); } + +void CC1101Component::set_tuner_bandwidth(float value) { + CHECK_FLOAT_RANGE(value, BANDWIDTH_MIN, BANDWIDTH_MAX) + + uint8_t e; + uint32_t m; + split_float(XTAL_FREQUENCY / (value * 8), 2, e, m); + this->state_.CHANBW_E = (uint8_t) e; + this->state_.CHANBW_M = (uint8_t) m; + + ESP_LOGD(TAG, "set_tuner_bandwidth(%f) CHANBW_E = %d CHANBW_M = %d", value, this->state_.CHANBW_E, + this->state_.CHANBW_M); + + this->publish_tuner_bandwidth(); + + if (!this->reset_) { + return; + } + + this->write_(Register::MDMCFG4); +} + +float CC1101Component::get_tuner_bandwidth() { + float chanbw = (float) (4 + this->state_.CHANBW_M) * (1 << this->state_.CHANBW_E); + return roundf(XTAL_FREQUENCY / (8 * chanbw)); +} + +void CC1101Component::set_tuner_channel(uint8_t value) { + this->state_.CHANNR = (uint8_t) value; + + ESP_LOGD(TAG, "set_tuner_channel(%d)", (int) value); + + this->publish_tuner_channel(); + + if (!this->reset_) { + return; + } + + this->write_(Register::CHANNR); +} + +uint8_t CC1101Component::get_tuner_channel() { return (uint8_t) this->state_.CHANNR; } + +void CC1101Component::set_tuner_channel_spacing(float value) { + CHECK_FLOAT_RANGE(value, CHANNEL_SPACING_MIN, CHANNEL_SPACING_MAX) + + uint8_t e; + uint32_t m; + split_float(value * (1 << 18) / XTAL_FREQUENCY, 8, e, m); + this->state_.CHANSPC_E = (uint8_t) e; + this->state_.CHANSPC_M = (uint8_t) m; + + ESP_LOGD(TAG, "set_tuner_channel_spacing(%f) CHANSPC_E = %d CHANSPC_M = %d", value, this->state_.CHANSPC_E, + this->state_.CHANSPC_M); + + this->publish_tuner_channel_spacing(); + + if (!this->reset_) { + return; + } + + this->write_(Register::MDMCFG1); + this->write_(Register::MDMCFG0); +} + +float CC1101Component::get_tuner_channel_spacing() { + float chanspc = (float) (256 + this->state_.CHANSPC_M) * (1 << this->state_.CHANSPC_E); + return XTAL_FREQUENCY * chanspc / (1 << 18); +} + +void CC1101Component::set_tuner_fsk_deviation(float value) { + CHECK_FLOAT_RANGE(value, FSK_DEVIATION_MIN, FSK_DEVIATION_MAX) + + uint8_t e; + uint32_t m; + split_float(value * (1 << 17) / XTAL_FREQUENCY, 3, e, m); + this->state_.DEVIATION_E = (uint8_t) e; + this->state_.DEVIATION_M = (uint8_t) m; + + ESP_LOGD(TAG, "set_tuner_fsk_deviation(%f) DEVIATION_E = %d DEVIATION_M = %d", value, this->state_.DEVIATION_E, + this->state_.DEVIATION_M); + + this->publish_tuner_fsk_deviation(); + + if (!this->reset_) { + return; + } + + this->write_(Register::DEVIATN); +} + +float CC1101Component::get_tuner_fsk_deviation() { + float deviation = (float) (8 + this->state_.DEVIATION_M) * (1 << this->state_.DEVIATION_E); + return XTAL_FREQUENCY * deviation / (1 << 17); +} + +void CC1101Component::set_tuner_msk_deviation(uint8_t value) { + CHECK_INT_RANGE(value, MSK_DEVIATION_MIN, MSK_DEVIATION_MAX) + + this->state_.DEVIATION_E = 0; + this->state_.DEVIATION_M = value - 1; // Specifies the fraction of symbol period (1/8-8/8) + + ESP_LOGD(TAG, "set_tuner_msk_deviation(%d)", value); + + this->publish_tuner_msk_deviation(); + + if (!this->reset_) { + return; + } + + this->write_(Register::DEVIATN); +} + +uint8_t CC1101Component::get_tuner_msk_deviation() { return this->state_.DEVIATION_M + 1; } + +void CC1101Component::set_tuner_symbol_rate(float value) { + CHECK_FLOAT_RANGE(value, SYMBOL_RATE_MIN, SYMBOL_RATE_MAX) + + uint8_t e; + uint32_t m; + split_float(value * (1 << 28) / (XTAL_FREQUENCY * 1000), 8, e, m); + this->state_.DRATE_E = (uint8_t) e; + this->state_.DRATE_M = (uint8_t) m; + + ESP_LOGD(TAG, "set_tuner_symbol_rate(%f) CHANBW_E = %d CHANBW_M = %d", value, this->state_.DRATE_E, + this->state_.DRATE_M); + + this->publish_tuner_symbol_rate(); + + if (!this->reset_) { + return; + } + + this->write_(Register::MDMCFG4); + this->write_(Register::MDMCFG3); +} + +float CC1101Component::get_tuner_symbol_rate() { + float drate = (float) (256 + this->state_.DRATE_M) * (1 << this->state_.DRATE_E); + return (XTAL_FREQUENCY * 1000) * drate / (1 << 28); +} + +void CC1101Component::set_tuner_sync_mode(SyncMode value) { + CHECK_ENUM(value) + + this->state_.SYNC_MODE = (uint8_t) value; + + ESP_LOGD(TAG, "set_tuner_sync_mode(%d)", (int) value); + + this->publish_tuner_sync_mode(); + + if (!this->reset_) { + return; + } + + this->write_(Register::MDMCFG2); +} + +SyncMode CC1101Component::get_tuner_sync_mode() { return (SyncMode) this->state_.SYNC_MODE; } + +void CC1101Component::set_tuner_carrier_sense_above_threshold(bool value) { + this->state_.CARRIER_SENSE_ABOVE_THRESHOLD = value ? 1 : 0; + + ESP_LOGD(TAG, "set_tuner_carrier_sense_above_threshold(%d)", value ? 1 : 0); + + this->publish_tuner_carrier_sense_above_threshold(); + + if (!this->reset_) { + return; + } + + this->write_(Register::MDMCFG2); +} + +bool CC1101Component::get_tuner_carrier_sense_above_threshold() { + return this->state_.CARRIER_SENSE_ABOVE_THRESHOLD == 1; +} + +void CC1101Component::set_tuner_modulation(Modulation value) { + CHECK_ENUM(value) + + this->state_.MOD_FORMAT = (uint8_t) value; + // In OOK/ASK mode, this selects the PATABLE index to use when transmitting a ‘1’. + // PATABLE index zero is used in OOK/ASK when transmitting a ‘0’. + this->state_.PA_POWER = value == Modulation::MODULATION_ASK_OOK ? 1 : 0; + + this->set_output_power(this->output_power_requested_); // frequency and modulation dependent + + ESP_LOGD(TAG, "set_tuner_modulation(%d)", (int) value); + + this->publish_tuner_modulation(); + + if (!this->reset_) { + return; + } + + this->write_(Register::MDMCFG2); + this->write_(Register::FREND0); +} + +Modulation CC1101Component::get_tuner_modulation() { return (Modulation) this->state_.MOD_FORMAT; } + +// agc_* + +void CC1101Component::set_agc_magn_target(MagnTarget value) { + CHECK_ENUM(value) + + this->state_.MAGN_TARGET = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_magn_target(%d)", (int) value); + + this->publish_agc_magn_target(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL2); +} + +MagnTarget CC1101Component::get_agc_magn_target() { return (MagnTarget) this->state_.MAGN_TARGET; } + +void CC1101Component::set_agc_max_lna_gain(MaxLnaGain value) { + CHECK_ENUM(value) + + this->state_.MAX_LNA_GAIN = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_max_lna_gain(%d)", (int) value); + + this->publish_agc_max_lna_gain(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL2); +} + +MaxLnaGain CC1101Component::get_agc_max_lna_gain() { return (MaxLnaGain) this->state_.MAX_LNA_GAIN; } + +void CC1101Component::set_agc_max_dvga_gain(MaxDvgaGain value) { + CHECK_ENUM(value) + + this->state_.MAX_DVGA_GAIN = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_max_dvga_gain(%d)", (int) value); + + this->publish_agc_max_dvga_gain(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL2); +} + +MaxDvgaGain CC1101Component::get_agc_max_dvga_gain() { return (MaxDvgaGain) this->state_.MAX_DVGA_GAIN; } + +void CC1101Component::set_agc_carrier_sense_abs_thr(int8_t value) { + CHECK_INT_RANGE(value, CARRIER_SENSE_ABS_THR_MIN, CARRIER_SENSE_ABS_THR_MAX) + + this->state_.CARRIER_SENSE_ABS_THR = (uint8_t) (value & 0b1111); + + ESP_LOGD(TAG, "set_agc_carrier_sense_abs_thr(%d)", (int) value); + + this->publish_agc_carrier_sense_abs_thr(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL1); +} + +int8_t CC1101Component::get_agc_carrier_sense_abs_thr() { + return (int8_t) (this->state_.CARRIER_SENSE_ABS_THR << 4) >> 4; +} + +void CC1101Component::set_agc_carrier_sense_rel_thr(CarrierSenseRelThr value) { + CHECK_ENUM(value) + + this->state_.CARRIER_SENSE_REL_THR = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_agc_carrier_sense_rel_thr(%d)", (int) value); + + this->publish_agc_carrier_sense_rel_thr(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL1); +} + +CarrierSenseRelThr CC1101Component::get_agc_carrier_sense_rel_thr() { + return (CarrierSenseRelThr) this->state_.CARRIER_SENSE_REL_THR; +} + +void CC1101Component::set_agc_lna_priority(bool value) { + this->state_.AGC_LNA_PRIORITY = value ? 1 : 0; + + ESP_LOGD(TAG, "set_agc_lna_priority(%d)", value ? 1 : 0); + + this->publish_agc_lna_priority(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL1); +} + +bool CC1101Component::get_agc_lna_priority() { return this->state_.AGC_LNA_PRIORITY == 1; } + +void CC1101Component::set_agc_filter_length_fsk_msk(FilterLengthFskMsk value) { + CHECK_ENUM(value) + + this->state_.FILTER_LENGTH = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_filter_length_fsk_msk(%d)", (int) value); + + this->publish_agc_filter_length_fsk_msk(); + this->publish_agc_filter_length_ask_ook(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL0); +} + +FilterLengthFskMsk CC1101Component::get_agc_filter_length_fsk_msk() { + return (FilterLengthFskMsk) this->state_.FILTER_LENGTH; +} + +void CC1101Component::set_agc_filter_length_ask_ook(FilterLengthAskOok value) { + CHECK_ENUM(value) + + this->state_.FILTER_LENGTH = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_filter_length_ask_ook(%d)", (int) value); + + this->publish_agc_filter_length_ask_ook(); + this->publish_agc_filter_length_fsk_msk(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL0); +} + +FilterLengthAskOok CC1101Component::get_agc_filter_length_ask_ook() { + return (FilterLengthAskOok) this->state_.FILTER_LENGTH; +} + +void CC1101Component::set_agc_freeze(Freeze value) { + CHECK_ENUM(value) + + this->state_.AGC_FREEZE = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_freeze(%d)", (int) value); + + this->publish_agc_freeze(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL0); +} + +Freeze CC1101Component::get_agc_freeze() { return (Freeze) this->state_.AGC_FREEZE; } + +void CC1101Component::set_agc_wait_time(WaitTime value) { + CHECK_ENUM(value) + + this->state_.WAIT_TIME = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_wait_time(%d)", (int) value); + + this->publish_agc_wait_time(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL0); +} + +WaitTime CC1101Component::get_agc_wait_time() { return (WaitTime) this->state_.WAIT_TIME; } + +void CC1101Component::set_agc_hyst_level(HystLevel value) { + CHECK_ENUM(value) + + this->state_.HYST_LEVEL = (uint8_t) value; + + ESP_LOGD(TAG, "set_agc_hyst_level(%d)", (int) value); + + this->publish_agc_hyst_level(); + + if (!this->reset_) { + return; + } + + this->write_(Register::AGCCTRL0); +} + +HystLevel CC1101Component::get_agc_hyst_level() { return (HystLevel) this->state_.HYST_LEVEL; } + +// sensors + +std::string CC1101Component::get_chip_id_text_sensor() { return this->chip_id_; } + +float CC1101Component::get_rssi_sensor() { return (float) this->state_.RSSI / 2 - 74; } + +float CC1101Component::get_lqi_sensor() { return (float) this->state_.LQI_EST; } + +float CC1101Component::get_temperature_sensor() { return (float) 0; } // TODO + +template void CC1101Component::publish_(S *s, T state) { + if (s != nullptr) { + if (!s->has_state() || s->state != state) { + s->publish_state(state); + } + } +} + +void CC1101Component::publish_switch_(switch_::Switch *s, bool state) { + if (s != nullptr) { + if (s->state != state) { // ? + s->publish_state(state); + } + } +} + +void CC1101Component::publish_select_(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); + } + } + } +} + +} // namespace cc1101 +} // namespace esphome diff --git a/components/cc1101/cc1101.h b/components/cc1101/cc1101.h new file mode 100644 index 0000000..418c41a --- /dev/null +++ b/components/cc1101/cc1101.h @@ -0,0 +1,125 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/spi/spi.h" +#include "esphome/components/remote_base/rc_switch_protocol.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/text_sensor/text_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 +#include "cc1101defs.h" +#include "cc1101sub.h" + +namespace esphome { +namespace cc1101 { + +class CC1101Component : public PollingComponent, + public spi::SPIDevice { + public: + CC1101Component(); + + void set_config_gdo0_pin(InternalGPIOPin *pin) { gdo0_ = pin; } + void set_config_gdo0_adc_pin(voltage_sampler::VoltageSampler *pin) { gdo0_adc_ = pin; } + + void setup() override; + void dump_config() override; + void update() override; + void loop() override; + + void begin_tx(); + void end_tx(); + + CC1101_SUB_NUMBER(output_power, float) + CC1101_SUB_SELECT(rx_attenuation, RxAttenuation) + CC1101_SUB_SWITCH(dc_blocking_filter) + // TODO: CC1101_SUB_SWITCH(manchester) + CC1101_SUB_NUMBER(tuner_frequency, float) + CC1101_SUB_NUMBER(tuner_if_frequency, float) + CC1101_SUB_NUMBER(tuner_bandwidth, float) + CC1101_SUB_NUMBER(tuner_channel, uint8_t) + CC1101_SUB_NUMBER(tuner_channel_spacing, float) + CC1101_SUB_NUMBER(tuner_fsk_deviation, float) + CC1101_SUB_NUMBER(tuner_msk_deviation, uint8_t) + CC1101_SUB_NUMBER(tuner_symbol_rate, float) + CC1101_SUB_SELECT(tuner_sync_mode, SyncMode) + CC1101_SUB_SWITCH(tuner_carrier_sense_above_threshold) + CC1101_SUB_SELECT(tuner_modulation, Modulation) + CC1101_SUB_SELECT(agc_magn_target, MagnTarget) + CC1101_SUB_SELECT(agc_max_lna_gain, MaxLnaGain) + CC1101_SUB_SELECT(agc_max_dvga_gain, MaxDvgaGain) + CC1101_SUB_NUMBER(agc_carrier_sense_abs_thr, int8_t) + CC1101_SUB_SELECT(agc_carrier_sense_rel_thr, CarrierSenseRelThr) + CC1101_SUB_SWITCH(agc_lna_priority) + CC1101_SUB_SELECT(agc_filter_length_fsk_msk, FilterLengthFskMsk) + CC1101_SUB_SELECT(agc_filter_length_ask_ook, FilterLengthAskOok) + CC1101_SUB_SELECT(agc_freeze, Freeze) + CC1101_SUB_SELECT(agc_wait_time, WaitTime) + CC1101_SUB_SELECT(agc_hyst_level, HystLevel) + CC1101_SUB_TEXT_SENSOR(chip_id) + // TODO: PARTNUM + // TODO: VERSION + CC1101_SUB_SENSOR(rssi) + CC1101_SUB_SENSOR(lqi) + // TODO: CRC OK + CC1101_SUB_SENSOR(temperature) + + protected: + InternalGPIOPin *gdo0_; + voltage_sampler::VoltageSampler *gdo0_adc_; + std::string chip_id_; + bool reset_; + float output_power_requested_; + float output_power_effective_; + uint8_t pa_table_[8]; + union { + struct CC1101State state_; + uint8_t regs_[sizeof(struct CC1101State) / sizeof(uint8_t)]; + }; + + void strobe_(Command cmd); + void write_(Register reg); + void write_(Register reg, uint8_t value); + void write_(Register reg, uint8_t *buffer, size_t length); + bool read_(Register reg); + bool read_(Register reg, uint8_t *buffer, size_t length); + // bool send_data_(const uint8_t* data, size_t length); + void send_(Command cmd); + bool wait_(Command cmd); + + template void publish_(S *s, T state); + // template specialization here in the header is not supported by the compiler + void publish_switch_(switch_::Switch *s, bool state); + void publish_select_(select::Select *s, size_t index); +}; + +template class BeginTxAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->begin_tx(); } +}; + +template class EndTxAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->end_tx(); } +}; + +template +class CC1101RawAction : public remote_base::RCSwitchRawAction, public Parented { + protected: + void play(Ts... x) override { + this->parent_->begin_tx(); + remote_base::RCSwitchRawAction::play(x...); + this->parent_->end_tx(); + } + + public: +}; + +} // namespace cc1101 +} // namespace esphome diff --git a/components/cc1101/cc1101defs.h b/components/cc1101/cc1101defs.h new file mode 100644 index 0000000..e46d6c8 --- /dev/null +++ b/components/cc1101/cc1101defs.h @@ -0,0 +1,693 @@ +#pragma once + +#include + +namespace esphome { +namespace cc1101 { + +static constexpr float XTAL_FREQUENCY = 26000; +static constexpr float OUTPUT_POWER_MIN = -30; +static constexpr float OUTPUT_POWER_MAX = 11; +static constexpr float FREQUENCY_MIN = 300000; +static constexpr float FREQUENCY_MAX = 928000; +static constexpr float IF_FREQUENCY_MIN = 25; +static constexpr float IF_FREQUENCY_MAX = 788; +static constexpr float BANDWIDTH_MIN = 58; +static constexpr float BANDWIDTH_MAX = 812; +static constexpr uint8_t CHANNEL_MIN = 0; +static constexpr uint8_t CHANNEL_MAX = 255; +static constexpr float CHANNEL_SPACING_MIN = 25; +static constexpr float CHANNEL_SPACING_MAX = 405; +static constexpr float FSK_DEVIATION_MIN = 1.5f; +static constexpr float FSK_DEVIATION_MAX = 381; +static constexpr uint8_t MSK_DEVIATION_MIN = 1; +static constexpr uint8_t MSK_DEVIATION_MAX = 8; +static constexpr float SYMBOL_RATE_MIN = 600; +static constexpr float SYMBOL_RATE_MAX = 500000; +static constexpr int8_t CARRIER_SENSE_ABS_THR_MIN = -8; +static constexpr int8_t CARRIER_SENSE_ABS_THR_MAX = 7; + +static constexpr uint8_t BUS_BURST = 0x40; +static constexpr uint8_t BUS_READ = 0x80; +static constexpr uint8_t BUS_WRITE = 0x00; +static constexpr uint8_t BYTES_IN_RXFIFO = 0x7F; // byte number in RXfifo + +enum class Register : uint8_t { + IOCFG2, // GDO2 output pin configuration + IOCFG1, // GDO1 output pin configuration + IOCFG0, // GDO0 output pin configuration + FIFOTHR, // RX FIFO and TX FIFO thresholds + SYNC1, // Sync word, high INT8U + SYNC0, // Sync word, low INT8U + PKTLEN, // Packet length + PKTCTRL1, // Packet automation control + PKTCTRL0, // Packet automation control + ADDR, // Device address + CHANNR, // Channel number + FSCTRL1, // Frequency synthesizer control + FSCTRL0, // Frequency synthesizer control + FREQ2, // Frequency control word, high INT8U + FREQ1, // Frequency control word, middle INT8U + FREQ0, // Frequency control word, low INT8U + MDMCFG4, // Modem configuration + MDMCFG3, // Modem configuration + MDMCFG2, // Modem configuration + MDMCFG1, // Modem configuration + MDMCFG0, // Modem configuration + DEVIATN, // Modem deviation setting + MCSM2, // Main Radio Control State Machine configuration + MCSM1, // Main Radio Control State Machine configuration + MCSM0, // Main Radio Control State Machine configuration + FOCCFG, // Frequency Offset Compensation configuration + BSCFG, // Bit Synchronization configuration + AGCCTRL2, // AGC control + AGCCTRL1, // AGC control + AGCCTRL0, // AGC control + WOREVT1, // High INT8U Event 0 timeout + WOREVT0, // Low INT8U Event 0 timeout + WORCTRL, // Wake On Radio control + FREND1, // Front end RX configuration + FREND0, // Front end TX configuration + FSCAL3, // Frequency synthesizer calibration + FSCAL2, // Frequency synthesizer calibration + FSCAL1, // Frequency synthesizer calibration + FSCAL0, // Frequency synthesizer calibration + RCCTRL1, // RC oscillator configuration + RCCTRL0, // RC oscillator configuration + FSTEST, // Frequency synthesizer calibration control + PTEST, // Production test + AGCTEST, // AGC test + TEST2, // Various test settings + TEST1, // Various test settings + TEST0, // Various test settings + UNUSED, + PARTNUM, + VERSION, + FREQEST, + LQI, + RSSI, + MARCSTATE, + WORTIME1, + WORTIME0, + PKTSTATUS, + VCO_VC_DAC, + TXBYTES, + RXBYTES, + RCCTRL1_STATUS, + RCCTRL0_STATUS, + PATABLE, + FIFO, +}; + +enum class Command : uint8_t { + RES = 0x30, // Reset chip. + FSTXON, // Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). + // If in RX/TX: Go to a wait state where only the synthesizer is + // running (for quick RX / TX turnaround). + XOFF, // Turn off crystal oscillator. + CAL, // Calibrate frequency synthesizer and turn it off + // (enables quick start). + RX, // Enable RX. Perform calibration first if coming from IDLE and + // MCSM0.FS_AUTOCAL=1. + TX, // In IDLE state: Enable TX. Perform calibration first if + // MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled: + // Only go to TX if channel is clear. + IDLE, // Exit RX / TX, turn off frequency synthesizer and exit + // Wake-On-Radio mode if applicable. + AFC, // Perform AFC adjustment of the frequency synthesizer + WOR, // Start automatic RX polling sequence (Wake-on-Radio) + PWD, // Enter power down mode when CSn goes high. + FRX, // Flush the RX FIFO buffer. + FTX, // Flush the TX FIFO buffer. + WORRST, // Reset real time clock. + NOP, // No operation. May be used to pad strobe commands to two + // INT8Us for simpler software. +}; + +enum class State : uint8_t { + SLEEP, + IDLE, + XOFF, + VCOON_MC, + REGON_MC, + MANCAL, + VCOON, + REGON, + STARTCAL, + BWBOOST, + FS_LOCK, + IFADCON, + ENDCAL, + RX, + RX_END, + RX_RST, + TXRX_SWITCH, + RXFIFO_OVERFLOW, + FSTXON, + TX, + TX_END, + RXTX_SWITCH, + TXFIFO_UNDERFLOW, +}; + +enum class RxAttenuation : uint8_t { + RX_ATTENUATION_0DB, + RX_ATTENUATION_6DB, + RX_ATTENUATION_12DB, + RX_ATTENUATION_18DB, + LAST, +}; + +// MDMCFG2 + +enum class SyncMode : uint8_t { + SYNC_MODE_NONE, + SYNC_MODE_15_16, + SYNC_MODE_16_16, + SYNC_MODE_30_32, + LAST, +}; + +enum class Modulation : uint8_t { + MODULATION_2_FSK, + MODULATION_GFSK, + MODULATION_UNUSED_2, + MODULATION_ASK_OOK, + MODULATION_4_FSK, + MODULATION_UNUSED_5, + MODULATION_UNUSED_6, + MODULATION_MSK, + LAST, +}; + +// AGCCTRL2 + +enum class MagnTarget : uint8_t { + MAGN_TARGET_24DB, + MAGN_TARGET_27DB, + MAGN_TARGET_30DB, + MAGN_TARGET_33DB, + MAGN_TARGET_36DB, + MAGN_TARGET_38DB, + MAGN_TARGET_40DB, + MAGN_TARGET_42DB, + LAST, +}; + +enum class MaxLnaGain : uint8_t { + MAX_LNA_GAIN_DEFAULT, + MAX_LNA_GAIN_MINUS_2P6DB, + MAX_LNA_GAIN_MINUS_6P1DB, + MAX_LNA_GAIN_MINUS_7P4DB, + MAX_LNA_GAIN_MINUS_9P2DB, + MAX_LNA_GAIN_MINUS_11P5DB, + MAX_LNA_GAIN_MINUS_14P6DB, + MAX_LNA_GAIN_MINUS_17P1DB, + LAST, +}; + +enum class MaxDvgaGain : uint8_t { + MAX_DVGA_GAIN_DEFAULT, + MAX_DVGA_GAIN_MINUS_1, + MAX_DVGA_GAIN_MINUS_2, + MAX_DVGA_GAIN_MINUS_3, + LAST, +}; + +// AGCCTRL1 + +// CARRIER_SENSE_ABS_THR => number -7..+7, -8 is disabled + +enum class CarrierSenseRelThr : uint8_t { + CARRIER_SENSE_REL_THR_DEFAULT, + CARRIER_SENSE_REL_THR_PLUS_6DB, + CARRIER_SENSE_REL_THR_PLUS_10DB, + CARRIER_SENSE_REL_THR_PLUS_14DB, + LAST, +}; + +// AGC_LNA_PRIORITY => switch + +// AGCCTRL0 + +enum class FilterLengthFskMsk : uint8_t { + FILTER_LENGTH_8DB, + FILTER_LENGTH_16DB, + FILTER_LENGTH_32DB, + FILTER_LENGTH_64DB, + LAST, +}; + +enum class FilterLengthAskOok : uint8_t { + FILTER_LENGTH_4DB, + FILTER_LENGTH_8DB, + FILTER_LENGTH_12DB, + FILTER_LENGTH_16DB, + LAST, +}; + +enum class Freeze : uint8_t { + FREEZE_DEFAULT, + FREEZE_ON_SYNC, + FREEZE_ANALOG_ONLY, + FREEZE_ANALOG_AND_DIGITAL, + LAST, +}; + +enum class WaitTime : uint8_t { + WAIT_TIME_8_SAMPLES, + WAIT_TIME_16_SAMPLES, + WAIT_TIME_24_SAMPLES, + WAIT_TIME_32_SAMPLES, + LAST, +}; + +enum class HystLevel : uint8_t { + HYST_LEVEL_NONE, + HYST_LEVEL_LOW, + HYST_LEVEL_MEDIUM, + HYST_LEVEL_HIGH, + LAST, +}; + +// + +struct CC1101State { + // 0x00 + union { + uint8_t IOCFG2; + struct { + uint8_t GDO2_CFG : 6; + uint8_t GDO2_INV : 1; + uint8_t : 1; + }; + }; + // 0x01 + union { + uint8_t IOCFG1; + struct { + uint8_t GDO1_CFG : 6; + uint8_t GDO1_INV : 1; + uint8_t GDO_DS : 1; // GDO, not GD0 + }; + }; + // 0x02 + union { + uint8_t IOCFG0; + struct { + uint8_t GDO0_CFG : 6; + uint8_t GDO0_INV : 1; + uint8_t TEMP_SENSOR_ENABLE : 1; + }; + }; + // 0x03 + union { + uint8_t FIFOTHR; + struct { + uint8_t FIFO_THR : 4; + uint8_t CLOSE_IN_RX : 2; // RxAttenuation + uint8_t ADC_RETENTION : 1; + uint8_t : 1; + }; + }; + // 0x04 + uint8_t SYNC1; + // 0x05 + uint8_t SYNC0; + // 0x06 + uint8_t PKTLEN; + // 0x07 + union { + uint8_t PKTCTRL1; + struct { + uint8_t ADR_CHK : 2; + uint8_t APPEND_STATUS : 1; + uint8_t CRC_AUTOFLUSH : 1; + uint8_t : 1; + uint8_t PQT : 3; + }; + }; + // 0x08 + union { + uint8_t PKTCTRL0; + struct { + uint8_t LENGTH_CONFIG : 2; + uint8_t CRC_EN : 1; + uint8_t : 1; + uint8_t PKT_FORMAT : 2; + uint8_t WHITE_DATA : 1; + uint8_t : 1; + }; + }; + // 0x09 + uint8_t ADDR; + // 0x0A + uint8_t CHANNR; + // 0x0B + union { + uint8_t FSCTRL1; + struct { + uint8_t FREQ_IF : 5; + uint8_t RESERVED : 1; // hm? + uint8_t : 2; + }; + }; + // 0x0C + uint8_t FSCTRL0; + // 0x0D + uint8_t FREQ2; // [7:6] always zero + // 0x0E + uint8_t FREQ1; + // 0x0F + uint8_t FREQ0; + // 0x10 + union { + uint8_t MDMCFG4; + struct { + uint8_t DRATE_E : 4; + uint8_t CHANBW_M : 2; + uint8_t CHANBW_E : 2; + }; + }; + // 0x11 + union { + uint8_t MDMCFG3; + struct { + uint8_t DRATE_M : 8; + }; + }; + // 0x12 + union { + uint8_t MDMCFG2; + struct { + uint8_t SYNC_MODE : 2; + uint8_t CARRIER_SENSE_ABOVE_THRESHOLD : 1; + uint8_t MANCHESTER_EN : 1; + uint8_t MOD_FORMAT : 3; // Modulation + uint8_t DEM_DCFILT_OFF : 1; + }; + }; + // 0x13 + union { + uint8_t MDMCFG1; + struct { + uint8_t CHANSPC_E : 2; + uint8_t : 2; + uint8_t NUM_PREAMBLE : 3; + uint8_t FEC_EN : 1; + }; + }; + // 0x14 + union { + uint8_t MDMCFG0; + struct { + uint8_t CHANSPC_M : 8; + }; + }; + // 0x15 + union { + uint8_t DEVIATN; + struct { + uint8_t DEVIATION_M : 3; + uint8_t : 1; + uint8_t DEVIATION_E : 3; + uint8_t : 1; + }; + }; + // 0x16 + union { + uint8_t MCSM2; + struct { + uint8_t RX_TIME : 3; + uint8_t RX_TIME_QUAL : 1; + uint8_t RX_TIME_RSSI : 1; + uint8_t : 3; + }; + }; + // 0x17 + union { + uint8_t MCSM1; + struct { + uint8_t TXOFF_MODE : 2; + uint8_t RXOFF_MODE : 2; + uint8_t CCA_MODE : 2; + uint8_t : 2; + }; + }; + // 0x18 + union { + uint8_t MCSM0; + struct { + uint8_t XOSC_FORCE_ON : 1; + uint8_t PIN_CTRL_EN : 1; + uint8_t PO_TIMEOUT : 2; + uint8_t FS_AUTOCAL : 2; + uint8_t : 2; + }; + }; + // 0x19 + union { + uint8_t FOCCFG; + struct { + uint8_t FOC_LIMIT : 2; + uint8_t FOC_POST_K : 1; + uint8_t FOC_PRE_K : 2; + uint8_t FOC_BS_CS_GATE : 1; + uint8_t : 2; + }; + }; + // 0x1A + union { + uint8_t BSCFG; + struct { + uint8_t BS_LIMIT : 2; + uint8_t BS_POST_KP : 1; + uint8_t BS_POST_KI : 1; + uint8_t BS_PRE_KP : 2; + uint8_t BS_PRE_KI : 2; + }; + }; + // 0x1B + union { + uint8_t AGCCTRL2; + struct { + uint8_t MAGN_TARGET : 3; // MagnTarget + uint8_t MAX_LNA_GAIN : 3; // MaxLnaGain + uint8_t MAX_DVGA_GAIN : 2; // MaxDvgaGain + }; + }; + // 0x1C + union { + uint8_t AGCCTRL1; + struct { + uint8_t CARRIER_SENSE_ABS_THR : 4; + uint8_t CARRIER_SENSE_REL_THR : 2; // CarrierSenseRelThr + uint8_t AGC_LNA_PRIORITY : 1; + uint8_t : 1; + }; + }; + // 0x1D + union { + uint8_t AGCCTRL0; + struct { + uint8_t FILTER_LENGTH : 2; // FilterLengthFskMsk or FilterLengthAskOok + uint8_t AGC_FREEZE : 2; // Freeze + uint8_t WAIT_TIME : 2; // WaitTime + uint8_t HYST_LEVEL : 2; // HystLevel + }; + }; + // 0x1E + uint8_t WOREVT1; + // 0x1F + uint8_t WOREVT0; + // 0x20 + union { + uint8_t WORCTRL; + struct { + uint8_t WOR_RES : 2; + uint8_t : 1; + uint8_t RC_CAL : 1; + uint8_t EVENT1 : 3; + uint8_t RC_PD : 1; + }; + }; + // 0x21 + union { + uint8_t FREND1; + struct { + uint8_t MIX_CURRENT : 2; + uint8_t LODIV_BUF_CURRENT_RX : 2; + uint8_t LNA2MIX_CURRENT : 2; + uint8_t LNA_CURRENT : 2; + }; + }; + // 0x22 + union { + uint8_t FREND0; + struct { + uint8_t PA_POWER : 3; + uint8_t : 1; + uint8_t LODIV_BUF_CURRENT_TX : 2; + uint8_t : 2; + }; + }; + // 0x23 + union { + uint8_t FSCAL3; + struct { + uint8_t FSCAL3_LO : 4; + uint8_t CHP_CURR_CAL_EN : 2; // Disable charge pump calibration stage when 0. + uint8_t FSCAL3_HI : 2; + }; + }; + // 0x24 + union { + // uint8_t FSCAL2; + struct { + uint8_t FSCAL2 : 5; + uint8_t VCO_CORE_H_EN : 1; + uint8_t : 2; + }; + }; + // 0x25 + union { + // uint8_t FSCAL1; + struct { + uint8_t FSCAL1 : 6; + uint8_t : 2; + }; + }; + // 0x26 + union { + // uint8_t FSCAL0; + struct { + uint8_t FSCAL0 : 7; + uint8_t : 1; + }; + }; + // 0x27 + union { + // uint8_t RCCTRL1; + struct { + uint8_t RCCTRL1 : 7; + uint8_t : 1; + }; + }; + // 0x28 + union { + // uint8_t RCCTRL0; + struct { + uint8_t RCCTRL0 : 7; + uint8_t : 1; + }; + }; + // 0x29 + uint8_t FSTEST; + // 0x2A + uint8_t PTEST; + // 0x2B + uint8_t AGCTEST; + // 0x2C + uint8_t TEST2; + // 0x2D + uint8_t TEST1; + // 0x2E + union { + uint8_t TEST0; + struct { + uint8_t TEST0_LO : 1; + uint8_t VCO_SEL_CAL_EN : 1; // Enable VCO selection calibration stage when 1 + uint8_t TEST0_HI : 6; + }; + }; + // 0x2F + uint8_t REG_2F; + // 0x30 + uint8_t PARTNUM; + // 0x31 + uint8_t VERSION; + // 0x32 + union { + uint8_t FREQEST; + struct { + int8_t FREQOFF_EST : 8; + }; + }; + // 0x33 + union { + uint8_t LQI; + struct { + uint8_t LQI_EST : 7; + uint8_t LQI_CRC_OK : 1; + }; + }; + // 0x34 + int8_t RSSI; + // 0x35 + union { + // uint8_t MARCSTATE; + struct { + uint8_t MARC_STATE : 5; // State + uint8_t : 3; + }; + }; + // 0x36 + uint8_t WORTIME1; + // 0x37 + uint8_t WORTIME0; + // 0x38 + union { + uint8_t PKTSTATUS; + struct { + uint8_t GDO0 : 1; + uint8_t : 1; + uint8_t GDO2 : 1; + uint8_t SFD : 1; + uint8_t CCA : 1; + uint8_t PQT_REACHED : 1; + uint8_t CS : 1; + uint8_t CRC_OK : 1; // same as LQI_CRC_OK? + }; + }; + // 0x39 + uint8_t VCO_VC_DAC; + // 0x3A + union { + uint8_t TXBYTES; + struct { + uint8_t NUM_TXBYTES : 7; + uint8_t TXFIFO_UNDERFLOW : 1; + }; + }; + // 0x3B + union { + uint8_t RXBYTES; + struct { + uint8_t NUM_RXBYTES : 7; + uint8_t RXFIFO_OVERFLOW : 1; + }; + }; + // 0x3C + union { + // uint8_t RCCTRL1_STATUS; + struct { + uint8_t RCCTRL1_STATUS : 7; + uint8_t : 1; + }; + }; + // 0x3D + union { + // uint8_t RCCTRL0_STATUS; + struct { + uint8_t RCCTRL0_STATUS : 7; + uint8_t : 1; + }; + }; + // 0x3E + uint8_t REG_3E; + // 0x3F + uint8_t REG_3F; +}; + +} // namespace cc1101 +} // namespace esphome diff --git a/components/cc1101/cc1101pa.h b/components/cc1101/cc1101pa.h new file mode 100644 index 0000000..ffe38f0 --- /dev/null +++ b/components/cc1101/cc1101pa.h @@ -0,0 +1,684 @@ +#pragma once + +#include +#include +#include + +namespace esphome { +namespace cc1101 { + +// CC1101 Design Note DN013 + +// The typical CC1101 output power levels and current consumption for the different PATABLE +// register settings are given in Table 1for 315 MHz, in Table 2 for 433 MHz, in Table 3 for 868 +// MHz, and in Table 4 for 915 MHz. It is however not recommended to use the PATABLE +// register settings from 0x61 to 0x6F for CC1101. + +struct PowerTableItem { + uint8_t value; + uint8_t dbm_diff; // starts from 12.0, diff to previous entry, scaled by 10 +}; + +struct PowerTable { + public: + static float find(const PowerTableItem *items, size_t count, float &dbm_target) { + int dbmi = 120; + int dbmi_target = (int) std::lround(dbm_target * 10); + for (size_t i = 0; i < count; i++) { + dbmi -= items[i].dbm_diff; + if (dbmi_target >= dbmi) { + if (items[i].value >= 0x61 && items[i].value <= 0x6F) + continue; // see note in the commend above + dbm_target = (float) dbmi / 10; + return items[i].value; + } + } + dbm_target = -30; + return 0x03; + } +}; + +// filtered tables to save memory +// - at least 1dBm difference +// - 0x61 - 0x6F removed +// - no values below -30dBm + +static const PowerTableItem PA_TABLE_315[] = { + {0xC0, 14}, // C0 10.6 -35.3 -44.4 -57.8 -53.8 -58.3 -57.2 -57.8 -56.7 28.5 + {0xC3, 10}, // C3 9.6 -39.2 -45.3 -59.0 -54.2 -59.0 -57.5 -58.3 -57.2 26.2 + {0xC6, 11}, // C6 8.5 -43.2 -46.3 -59.2 -54.7 -59.1 -57.7 -58.3 -57.4 24.4 + {0xC9, 10}, // C9 7.5 -47.0 -47.3 -58.9 -55.0 -59.0 -57.9 -58.4 -57.5 23.0 + {0x81, 12}, // 81 6.3 -49.2 -45.7 -57.3 -53.6 -59.0 -56.0 -56.5 -57.5 19.5 + {0x85, 13}, // 85 5.0 -51.0 -47.2 -59.8 -54.2 -59.0 -56.9 -57.9 -58.0 18.3 + {0x88, 11}, // 88 3.9 -46.6 -48.1 -60.0 -55.0 -58.9 -57.5 -58.2 -58.2 17.4 + {0xCF, 11}, // CF 2.8 -49.8 -51.3 -57.6 -56.8 -59.1 -58.4 -58.1 -58.3 18.0 + {0x8D, 11}, // 8D 1.7 -43.8 -49.5 -58.9 -56.3 -58.8 -58.2 -58.4 -58.5 15.8 + {0x50, 10}, // 50 0.7 -59.2 -51.2 -59.0 -56.5 -59.0 -58.3 -58.3 -58.2 15.3 + {0x40, 10}, // 40 -0.3 -58.2 -52.1 -59.4 -56.9 -59.0 -58.4 -58.4 -58.3 14.7 + {0x3D, 10}, // 3D -1.3 -54.4 -48.4 -59.8 -57.5 -58.9 -58.3 -58.5 -58.5 19.3 + {0x55, 10}, // 55 -2.3 -56.7 -53.6 -59.7 -57.5 -59.1 -58.7 -58.4 -58.4 13.7 + {0x39, 11}, // 39 -3.4 -50.9 -49.5 -59.8 -58.0 -59.0 -58.5 -58.4 -58.4 16.8 + {0x2B, 15}, // 2B -4.9 -51.2 -50.4 -59.9 -58.0 -58.9 -58.7 -58.3 -58.4 15.6 + {0x29, 16}, // 29 -6.5 -51.8 -51.6 -59.9 -58.4 -59.0 -58.8 -58.3 -58.3 14.7 + {0x28, 10}, // 28 -7.5 -52.2 -52.5 -60.0 -58.6 -59.0 -58.8 -58.2 -58.4 14.3 + {0x27, 11}, // 27 -8.6 -52.9 -53.1 -60.0 -58.8 -59.1 -58.8 -58.3 -58.5 13.9 + {0x26, 12}, // 26 -9.8 -53.6 -54.3 -60.1 -58.7 -59.0 -58.7 -58.4 -58.4 13.4 + {0x25, 13}, // 25 -11.1 -54.3 -55.5 -60.1 -58.8 -59.1 -58.8 -58.4 -58.4 13.0 + {0x33, 11}, // 33 -12.2 -55.0 -56.3 -60.0 -58.7 -59.0 -58.9 -58.4 -58.4 12.8 + {0x1F, 11}, // 1F -13.3 -55.6 -57.2 -60.0 -58.8 -58.9 -58.9 -58.3 -58.4 12.4 + {0x1D, 12}, // 1D -14.5 -56.0 -58.0 -60.0 -58.8 -59.1 -58.7 -58.4 -58.5 12.1 + {0x32, 11}, // 32 -15.6 -56.9 -58.8 -59.9 -58.8 -59.0 -58.8 -58.3 -58.5 12.2 + {0x1A, 10}, // 1A -16.6 -57.3 -59.5 -59.9 -58.8 -59.1 -58.8 -58.4 -58.4 11.8 + {0x18, 19}, // 18 -18.5 -57.8 -60.3 -60.0 -58.8 -59.0 -58.9 -58.2 -58.5 11.6 + {0x17, 11}, // 17 -19.6 -58.7 -60.9 -60.0 -58.7 -58.9 -58.9 -58.5 -58.4 11.4 + {0x0C, 11}, // C -20.7 -59.4 -61.1 -60.0 -58.8 -59.1 -58.9 -58.4 -58.3 11.3 + {0x0A, 15}, // A -22.2 -59.9 -61.9 -60.0 -58.9 -59.0 -58.9 -58.4 -58.5 11.2 + {0x08, 18}, // 8 -24.0 -60.5 -62.5 -60.0 -58.7 -59.1 -58.8 -58.3 -58.5 11.1 + {0x07, 11}, // 7 -25.1 -61.3 -62.9 -60.1 -58.8 -59.1 -58.8 -58.4 -58.4 11.0 + {0x06, 13}, // 6 -26.4 -61.6 -63.2 -60.1 -58.7 -59.0 -58.9 -58.5 -58.5 11.0 + {0x05, 13}, // 5 -27.7 -62.3 -63.4 -60.1 -58.7 -59.2 -58.8 -58.4 -58.5 10.9 + {0x04, 19}, // 4 -29.6 -62.7 -63.6 -59.9 -58.7 -59.0 -58.9 -58.4 -58.4 10.8 +}; + +static const PowerTableItem PA_TABLE_433[] = { + {0xC0, 21}, // C0 9.9 -43.4 -45.0 -53.9 -55.2 -55.8 -52.3 -55.6 29.1 + {0xC3, 11}, // C3 8.8 -49.3 -45.9 -55.9 -55.4 -57.2 -52.6 -57.5 26.9 + {0xC6, 10}, // C6 7.8 -56.2 -46.9 -56.9 -55.6 -58.2 -53.2 -57.9 25.2 + {0xC9, 10}, // C9 6.8 -56.1 -47.9 -57.3 -55.9 -58.5 -54.0 -56.9 23.8 + {0xCC, 10}, // CC 5.8 -52.8 -48.9 -57.0 -56.1 -58.4 -54.6 -56.2 22.6 + {0x85, 10}, // 85 4.8 -54.2 -53.0 -58.3 -55.0 -57.8 -56.8 -58.0 19.1 + {0x88, 12}, // 88 3.6 -56.2 -53.8 -58.3 -55.7 -58.1 -57.2 -58.2 18.2 + {0x8B, 13}, // 8B 2.3 -57.7 -54.5 -58.0 -56.3 -58.1 -57.5 -58.2 17.3 + {0x8E, 19}, // 8E 0.4 -58.0 -55.5 -57.8 -57.4 -58.2 -58.1 -58.4 16.2 + {0x40, 12}, // 40 -0.8 -59.7 -56.1 -58.2 -57.7 -58.4 -58.3 -58.2 15.4 + {0x3C, 13}, // 3C -2.1 -60.6 -57.3 -58.2 -58.0 -58.5 -58.4 -58.5 19.3 + {0x3A, 10}, // 3A -3.1 -59.5 -57.5 -58.3 -58.3 -58.6 -58.1 -58.6 18.1 + {0x8F, 15}, // 8F -4.6 -52.2 -57.7 -58.1 -58.8 -58.4 -58.7 -58.3 14.4 + {0x37, 10}, // 37 -5.6 -56.8 -58.3 -58.3 -58.8 -58.4 -58.5 -58.4 16.2 + {0x36, 12}, // 36 -6.8 -56.8 -58.9 -58.3 -58.8 -58.3 -58.5 -58.5 15.6 + {0x28, 10}, // 28 -7.8 -56.6 -59.0 -58.2 -59.0 -58.4 -58.5 -58.4 15.1 + {0x26, 21}, // 26 -9.9 -57.0 -59.4 -58.3 -59.0 -58.4 -58.7 -58.4 14.3 + {0x25, 15}, // 25 -11.4 -57.3 -59.7 -58.4 -59.0 -58.3 -58.7 -58.5 13.9 + {0x24, 19}, // 24 -13.3 -57.9 -59.9 -58.2 -59.0 -58.6 -58.7 -58.5 13.5 + {0x1E, 10}, // 1E -14.3 -58.4 -59.8 -58.2 -59.0 -58.4 -58.6 -58.6 13.2 + {0x1C, 12}, // 1C -15.5 -58.6 -59.9 -58.4 -58.8 -58.6 -58.8 -58.5 12.9 + {0x1A, 15}, // 1A -17.0 -59.4 -59.9 -58.3 -59.1 -58.5 -58.7 -58.4 12.7 + {0x18, 18}, // 18 -18.8 -60.2 -59.9 -58.2 -59.0 -58.5 -58.7 -58.6 12.5 + {0x17, 10}, // 17 -19.8 -60.6 -59.9 -58.2 -58.9 -58.4 -58.7 -58.4 12.4 + {0x0C, 12}, // C -21.0 -61.1 -59.9 -58.4 -59.0 -58.5 -58.7 -58.6 12.3 + {0x15, 15}, // 15 -22.5 -61.7 -60.0 -58.2 -59.1 -58.3 -58.6 -58.7 12.2 + {0x08, 18}, // 8 -24.3 -62.3 -59.9 -58.3 -59.0 -58.4 -58.8 -58.5 12.1 + {0x07, 10}, // 7 -25.3 -62.6 -59.9 -58.2 -59.0 -58.6 -58.7 -58.5 12.0 + {0x06, 12}, // 6 -26.5 -63.2 -59.9 -58.3 -58.9 -58.5 -58.6 -58.6 12.0 + {0x05, 14}, // 5 -27.9 -63.5 -59.8 -58.3 -59.1 -58.5 -58.7 -58.4 11.9 + {0x04, 16}, // 4 -29.5 -63.7 -59.9 -58.3 -58.9 -58.5 -58.5 -58.5 11.9 +}; + +static const PowerTableItem PA_TABLE_868[] = { + {0xC0, 13}, // C0 10.7 -35.1 -58.6 -58.6 -57.5 -50.0 34.2 + {0xC3, 11}, // C3 9.6 -41.5 -58.5 -58.3 -57.4 -54.4 31.6 + {0xC6, 11}, // C6 8.5 -47.7 -58.5 -58.3 -57.6 -55.0 29.5 + {0xC9, 10}, // C9 7.5 -44.4 -58.5 -58.5 -57.7 -53.6 27.8 + {0xCC, 10}, // CC 6.5 -40.6 -58.6 -58.4 -57.6 -52.5 26.3 + {0xCE, 10}, // CE 5.5 -38.5 -58.5 -58.4 -57.8 -52.2 25.0 + {0x84, 11}, // 84 4.4 -35.3 -58.7 -58.5 -57.8 -55.8 20.3 + {0x87, 10}, // 87 3.4 -39.4 -58.6 -58.6 -57.8 -55.7 19.5 + {0xCF, 10}, // CF 2.4 -36.6 -58.6 -58.4 -57.7 -53.6 22.0 + {0x8C, 13}, // 8C 1.1 -50.2 -58.6 -58.5 -57.7 -55.9 17.9 + {0x50, 14}, // 50 -0.3 -42.1 -58.5 -58.5 -57.6 -57.1 16.9 + {0x40, 12}, // 40 -1.5 -43.2 -58.5 -58.7 -57.7 -57.2 16.1 + {0x3F, 11}, // 3F -2.6 -53.7 -58.6 -58.5 -57.8 -57.5 21.4 + {0x55, 10}, // 55 -3.6 -44.9 -58.6 -58.4 -57.8 -57.5 15.0 + {0x57, 12}, // 57 -4.8 -46.0 -58.6 -58.5 -57.6 -57.4 14.5 + {0x8F, 12}, // 8F -6.0 -51.6 -58.5 -58.6 -57.7 -57.1 15.0 + {0x2A, 14}, // 2A -7.4 -49.3 -58.5 -58.6 -57.7 -57.4 16.2 + {0x28, 16}, // 28 -9.0 -49.0 -58.5 -58.6 -57.7 -57.4 15.4 + {0x26, 20}, // 26 -11.0 -49.2 -58.5 -58.5 -57.7 -57.4 14.6 + {0x25, 15}, // 25 -12.5 -49.5 -58.6 -58.6 -57.8 -57.3 14.1 + {0x24, 18}, // 24 -14.3 -50.2 -58.5 -58.4 -57.8 -57.4 13.7 + {0x1D, 14}, // 1D -15.7 -50.7 -58.6 -58.6 -57.8 -57.5 13.3 + {0x1B, 13}, // 1B -17.0 -51.3 -58.5 -58.4 -57.7 -57.5 13.1 + {0x19, 16}, // 19 -18.6 -52.0 -58.6 -58.5 -57.8 -57.5 12.9 + {0x22, 10}, // 22 -19.6 -52.5 -58.5 -58.6 -57.7 -57.4 12.9 + {0x0D, 15}, // D -21.1 -53.3 -58.6 -58.6 -57.8 -57.4 12.6 + {0x0B, 12}, // B -22.3 -53.9 -58.6 -58.5 -57.8 -57.4 12.5 + {0x09, 15}, // 9 -23.8 -54.7 -58.5 -58.5 -57.8 -57.5 12.4 + {0x21, 10}, // 21 -24.8 -55.1 -58.5 -58.5 -57.7 -57.5 12.5 + {0x13, 17}, // 13 -26.5 -55.9 -58.6 -58.5 -57.6 -57.6 12.3 + {0x05, 12}, // 5 -27.7 -56.4 -58.4 -58.4 -57.7 -57.5 12.2 + {0x12, 12}, // 12 -28.9 -57.1 -58.4 -58.5 -57.7 -57.3 12.2 +}; + +static const PowerTableItem PA_TABLE_915[] = { + {0xC0, 26}, // C0 9.4 -33.5 -58.5 -58.4 -55.8 -32.6 31.8 + {0xC3, 11}, // C3 8.3 -41.5 -58.6 -58.4 -56.3 -38.0 29.3 + {0xC6, 11}, // C6 7.2 -42.5 -58.5 -58.4 -56.7 -40.5 27.4 + {0xC9, 10}, // C9 6.2 -37.6 -58.6 -58.4 -57.2 -38.8 25.9 + {0xCD, 12}, // CD 5.0 -34.2 -58.6 -58.5 -57.5 -37.3 24.3 + {0x84, 11}, // 84 3.9 -32.0 -58.6 -58.4 -57.7 -40.1 19.7 + {0x87, 10}, // 87 2.9 -36.5 -58.4 -58.5 -57.7 -39.6 18.9 + {0x8A, 11}, // 8A 1.8 -42.2 -58.5 -58.4 -57.7 -39.6 18.1 + {0x8D, 13}, // 8D 0.5 -46.8 -58.5 -58.5 -57.7 -40.4 17.3 + {0x8E, 11}, // 8E -0.6 -46.6 -58.5 -58.5 -57.8 -41.1 16.7 + {0x51, 10}, // 51 -1.6 -38.7 -58.4 -58.5 -57.7 -46.9 16.0 + {0x3E, 11}, // 3E -2.7 -50.0 -58.5 -58.4 -57.6 -55.3 20.7 + {0x3B, 11}, // 3B -3.8 -50.7 -58.6 -58.4 -57.6 -55.2 18.9 + {0x39, 13}, // 39 -5.1 -50.0 -58.5 -58.5 -57.6 -54.0 17.7 + {0x2B, 13}, // 2B -6.4 -47.6 -58.4 -58.4 -57.8 -52.1 16.5 + {0x36, 15}, // 36 -7.9 -46.9 -58.5 -58.4 -57.7 -51.2 15.8 + {0x35, 14}, // 35 -9.3 -46.7 -58.6 -58.4 -57.7 -50.7 15.2 + {0x26, 16}, // 26 -10.9 -47.0 -58.6 -58.4 -57.8 -50.9 14.5 + {0x25, 14}, // 25 -12.3 -47.2 -58.6 -58.3 -57.7 -51.0 14.1 + {0x24, 18}, // 24 -14.1 -48.1 -58.4 -58.4 -57.8 -51.4 13.7 + {0x1D, 14}, // 1D -15.5 -48.7 -58.4 -58.5 -57.7 -51.9 13.2 + {0x1B, 13}, // 1B -16.8 -49.3 -58.6 -58.4 -57.8 -52.3 13.0 + {0x19, 15}, // 19 -18.3 -50.2 -58.5 -58.5 -57.6 -52.8 12.8 + {0x18, 10}, // 18 -19.3 -50.6 -58.5 -58.5 -57.7 -53.1 12.7 + {0x17, 10}, // 17 -20.3 -51.2 -58.6 -58.5 -57.8 -53.1 12.6 + {0x0C, 11}, // C -21.4 -51.8 -58.4 -58.5 -57.7 -53.4 12.5 + {0x0A, 13}, // A -22.7 -52.6 -58.5 -58.4 -57.7 -53.6 12.4 + {0x08, 16}, // 8 -24.3 -53.6 -58.4 -58.4 -57.6 -54.1 12.3 + {0x13, 19}, // 13 -26.2 -54.6 -58.4 -58.5 -57.7 -54.3 12.2 + {0x05, 11}, // 5 -27.3 -55.3 -58.4 -58.4 -57.8 -54.5 12.1 + {0x12, 13}, // 12 -28.6 -55.9 -58.6 -58.5 -57.7 -54.7 12.1 + {0x03, 12}, // 3 -29.8 -56.9 -58.5 -58.4 -57.7 -54.7 12.0 +}; +/* +static const PowerTableItem PA_TABLE_315_FULL[] = { + {0xC0, 4}, // C0 10.6 -35.3 -44.4 -57.8 -53.8 -58.3 -57.2 -57.8 -56.7 28.5 + {0xC1, 2}, // C1 10.3 -36.6 -44.7 -58.4 -54.0 -58.5 -57.4 -58.0 -56.8 27.6 + {0xC2, 4}, // C2 9.9 -37.8 -45.0 -58.8 -54.1 -59.0 -57.3 -58.3 -56.9 26.9 + {0xC3, 3}, // C3 9.6 -39.2 -45.3 -59.0 -54.2 -59.0 -57.5 -58.3 -57.2 26.2 + {0xC4, 4}, // C4 9.2 -40.5 -45.7 -59.1 -54.5 -58.9 -57.5 -58.4 -57.2 25.6 + {0xC5, 3}, // C5 8.8 -41.9 -46.0 -59.2 -54.6 -58.9 -57.6 -58.4 -57.3 25.0 + {0xC6, 3}, // C6 8.5 -43.2 -46.3 -59.2 -54.7 -59.1 -57.7 -58.3 -57.4 24.4 + {0xC7, 3}, // C7 8.2 -44.5 -46.6 -59.2 -55.0 -59.1 -57.9 -58.3 -57.3 23.9 + {0xC8, 2}, // C8 7.9 -45.7 -47.0 -59.1 -55.0 -59.1 -57.9 -58.4 -57.3 23.4 + {0xC9, 4}, // C9 7.5 -47.0 -47.3 -58.9 -55.0 -59.0 -57.9 -58.4 -57.5 23.0 + {0xCA, 2}, // CA 7.2 -48.0 -47.7 -58.8 -55.0 -59.0 -57.8 -58.4 -57.7 22.5 + {0xCB, 2}, // CB 6.9 -49.1 -47.9 -58.7 -55.4 -58.9 -58.1 -58.3 -57.6 22.1 + {0xCC, 3}, // CC 6.6 -50.0 -48.3 -58.7 -55.3 -59.0 -58.1 -58.4 -57.8 21.7 + {0x80, 0}, // 80 6.6 -46.9 -45.4 -56.3 -53.7 -59.0 -55.8 -56.3 -57.5 19.8 + {0x81, 2}, // 81 6.3 -49.2 -45.7 -57.3 -53.6 -59.0 -56.0 -56.5 -57.5 19.5 + {0xCD, 0}, // CD 6.3 -50.9 -48.6 -58.4 -55.5 -58.9 -58.0 -58.1 -57.8 21.3 + {0x82, 2}, // 82 6.0 -52.0 -46.2 -58.1 -53.8 -58.9 -56.4 -57.1 -57.7 19.2 + {0x83, 2}, // 83 5.8 -53.7 -46.5 -58.7 -53.8 -59.0 -56.6 -57.3 -57.8 18.9 + {0xCE, 2}, // CE 5.6 -51.9 -49.2 -58.2 -55.5 -58.9 -58.1 -58.3 -57.9 20.6 + {0x84, 1}, // 84 5.4 -53.0 -46.8 -59.5 -54.0 -59.0 -56.9 -57.5 -57.8 18.6 + {0x85, 4}, // 85 5.0 -51.0 -47.2 -59.8 -54.2 -59.0 -56.9 -57.9 -58.0 18.3 + {0x86, 2}, // 86 4.7 -49.3 -47.5 -60.0 -54.4 -59.0 -57.1 -58.0 -58.1 18.0 + {0x87, 4}, // 87 4.3 -47.8 -47.8 -60.0 -54.6 -59.0 -57.2 -58.2 -58.1 17.7 + {0x88, 3}, // 88 3.9 -46.6 -48.1 -60.0 -55.0 -58.9 -57.5 -58.2 -58.2 17.4 + {0x89, 3}, // 89 3.5 -45.8 -48.4 -59.9 -55.2 -58.9 -57.7 -58.3 -58.3 17.0 + {0x8A, 3}, // 8A 3.1 -45.0 -48.7 -59.9 -55.3 -59.0 -57.8 -58.2 -58.3 16.7 + {0xCF, 3}, // CF 2.8 -49.8 -51.3 -57.6 -56.8 -59.1 -58.4 -58.1 -58.3 18.0 + {0x8B, 0}, // 8B 2.7 -44.5 -48.9 -59.6 -55.8 -58.9 -57.7 -58.5 -58.3 16.4 + {0x8C, 5}, // 8C 2.2 -44.1 -49.2 -59.0 -56.1 -58.9 -57.9 -58.3 -58.4 16.1 + {0x8D, 5}, // 8D 1.7 -43.8 -49.5 -58.9 -56.3 -58.8 -58.2 -58.4 -58.5 15.8 + {0x50, 10}, // 50 0.7 -59.2 -51.2 -59.0 -56.5 -59.0 -58.3 -58.3 -58.2 15.3 + {0x8E, 0}, // 8E 0.6 -43.7 -50.0 -58.7 -56.7 -58.9 -58.3 -58.3 -58.5 15.2 + {0x60, 0}, // 60 0.5 -59.9 -51.3 -59.0 -56.6 -59.1 -58.2 -58.4 -58.4 15.2 + {0x51, 4}, // 51 0.1 -58.9 -51.6 -59.2 -56.8 -59.0 -58.4 -58.5 -58.4 15.0 + {0x61, 2}, // 61 -0.1 -59.3 -51.7 -59.2 -56.7 -59.0 -58.5 -58.4 -58.4 14.8 + {0x40, 1}, // 40 -0.3 -58.2 -52.1 -59.4 -56.9 -59.0 -58.4 -58.4 -58.3 14.7 + {0x52, 2}, // 52 -0.5 -58.2 -52.1 -59.3 -56.7 -58.8 -58.4 -58.4 -58.5 14.6 + {0x62, 1}, // 62 -0.7 -58.8 -52.3 -59.3 -57.0 -59.0 -58.4 -58.4 -58.3 14.5 + {0x3F, 1}, // 3F -0.8 -57.0 -48.3 -59.9 -57.3 -59.1 -58.3 -58.4 -58.5 20.5 + {0x3E, 1}, // 3E -1.0 -55.9 -48.4 -59.8 -57.4 -59.1 -58.4 -58.5 -58.3 19.9 + {0x53, 1}, // 53 -1.1 -57.6 -52.7 -59.3 -57.1 -59.1 -58.6 -58.4 -58.4 14.3 + {0x3D, 1}, // 3D -1.3 -54.4 -48.4 -59.8 -57.5 -58.9 -58.3 -58.5 -58.5 19.3 + {0x63, 0}, // 63 -1.3 -58.0 -52.8 -59.5 -57.3 -59.1 -58.5 -58.3 -58.4 14.2 + {0x3C, 3}, // 3C -1.7 -53.2 -48.6 -59.9 -57.6 -59.0 -58.4 -58.4 -58.5 18.6 + {0x54, 0}, // 54 -1.7 -57.2 -53.2 -59.3 -57.3 -59.0 -58.5 -58.3 -58.4 14.0 + {0x64, 1}, // 64 -1.9 -57.4 -53.3 -59.5 -57.4 -59.0 -58.5 -58.2 -58.3 13.9 + {0x3B, 2}, // 3B -2.1 -52.2 -48.7 -59.8 -57.7 -59.2 -58.5 -58.4 -58.5 18.0 + {0x55, 1}, // 55 -2.3 -56.7 -53.6 -59.7 -57.5 -59.1 -58.7 -58.4 -58.4 13.7 + {0x65, 2}, // 65 -2.5 -57.1 -53.7 -59.7 -57.7 -59.0 -58.7 -58.4 -58.4 13.7 + {0x2F, 1}, // 2F -2.6 -51.5 -48.7 -60.0 -57.9 -59.0 -58.4 -58.3 -58.3 17.3 + {0x3A, 1}, // 3A -2.7 -51.4 -49.1 -59.8 -57.8 -59.1 -58.5 -58.4 -58.4 17.4 + {0x56, 2}, // 56 -3.0 -56.6 -54.1 -59.7 -57.7 -58.9 -58.8 -58.4 -58.5 13.5 + {0x2E, 1}, // 2E -3.1 -51.3 -49.1 -60.0 -57.9 -59.0 -58.6 -58.3 -58.4 16.9 + {0x66, 0}, // 66 -3.1 -56.9 -54.3 -59.7 -57.8 -58.9 -58.8 -58.4 -58.4 13.4 + {0x39, 2}, // 39 -3.4 -50.9 -49.5 -59.8 -58.0 -59.0 -58.5 -58.4 -58.4 16.8 + {0x57, 1}, // 57 -3.5 -56.6 -54.4 -59.8 -57.9 -59.0 -58.7 -58.3 -58.4 13.3 + {0x2D, 1}, // 2D -3.6 -51.1 -49.4 -60.0 -58.1 -59.1 -58.6 -58.5 -58.4 16.4 + {0x67, 1}, // 67 -3.7 -56.8 -54.7 -59.9 -58.0 -59.0 -58.8 -58.4 -58.3 13.2 + {0x8F, 5}, // 8F -4.2 -45.9 -52.6 -58.4 -58.5 -59.0 -58.8 -58.3 -58.6 13.3 + {0x2C, 0}, // 2C -4.2 -51.0 -49.8 -60.0 -58.0 -59.0 -58.6 -58.4 -58.4 16.0 + {0x38, 0}, // 38 -4.3 -50.7 -50.2 -59.8 -58.0 -59.0 -58.6 -58.3 -58.5 16.1 + {0x68, 0}, // 68 -4.3 -56.9 -55.1 -59.7 -58.0 -59.0 -58.9 -58.5 -58.5 13.0 + {0x2B, 6}, // 2B -4.9 -51.2 -50.4 -59.9 -58.0 -58.9 -58.7 -58.3 -58.4 15.6 + {0x69, 0}, // 69 -4.9 -56.9 -55.5 -59.9 -58.3 -59.1 -58.9 -58.3 -58.5 12.8 + {0x37, 5}, // 37 -5.4 -51.3 -50.5 -60.0 -58.3 -59.1 -58.7 -58.4 -58.4 15.4 + {0x6A, 0}, // 6A -5.5 -56.9 -56.1 -59.8 -58.3 -59.0 -58.8 -58.4 -58.5 12.6 + {0x2A, 2}, // 2A -5.7 -51.5 -51.1 -60.0 -58.4 -59.1 -58.7 -58.3 -58.6 15.2 + {0x6B, 3}, // 6B -6.1 -57.0 -56.5 -59.9 -58.5 -59.0 -58.9 -58.3 -58.6 12.5 + {0x29, 4}, // 29 -6.5 -51.8 -51.6 -59.9 -58.4 -59.0 -58.8 -58.3 -58.3 14.7 + {0x6C, 2}, // 6C -6.7 -57.0 -57.0 -59.9 -58.5 -59.1 -58.9 -58.5 -58.4 12.3 + {0x36, 0}, // 36 -6.7 -51.9 -51.5 -60.0 -58.2 -59.0 -58.8 -58.4 -58.4 14.7 + {0x6D, 5}, // 6D -7.2 -57.3 -57.1 -60.0 -58.6 -58.9 -58.8 -58.4 -58.3 12.2 + {0x28, 2}, // 28 -7.5 -52.2 -52.5 -60.0 -58.6 -59.0 -58.8 -58.2 -58.4 14.3 + {0x35, 5}, // 35 -8.1 -52.7 -52.7 -60.0 -58.7 -59.0 -58.9 -58.3 -58.5 14.1 + {0x6E, 3}, // 6E -8.4 -58.0 -57.9 -60.0 -58.6 -59.1 -58.9 -58.3 -58.5 11.9 + {0x27, 1}, // 27 -8.6 -52.9 -53.1 -60.0 -58.8 -59.1 -58.8 -58.3 -58.5 13.9 + {0x26, 12}, // 26 -9.8 -53.6 -54.3 -60.1 -58.7 -59.0 -58.7 -58.4 -58.4 13.4 + {0x34, 0}, // 34 -9.9 -53.7 -54.4 -59.9 -58.8 -59.0 -58.7 -58.3 -58.4 13.5 + {0x25, 11}, // 25 -11.1 -54.3 -55.5 -60.1 -58.8 -59.1 -58.8 -58.4 -58.4 13.0 + {0x33, 10}, // 33 -12.2 -55.0 -56.3 -60.0 -58.7 -59.0 -58.9 -58.4 -58.4 12.8 + {0x24, 8}, // 24 -13.0 -55.4 -56.9 -60.1 -58.7 -59.1 -59.0 -58.3 -58.5 12.6 + {0x6F, 1}, // 6F -13.2 -58.3 -60.7 -59.8 -58.7 -59.1 -58.9 -58.3 -58.5 11.2 + {0x1F, 1}, // 1F -13.3 -55.6 -57.2 -60.0 -58.8 -58.9 -58.9 -58.3 -58.4 12.4 + {0x1E, 5}, // 1E -13.9 -55.8 -57.4 -60.0 -58.6 -59.1 -58.9 -58.4 -58.5 12.2 + {0x1D, 5}, // 1D -14.5 -56.0 -58.0 -60.0 -58.8 -59.1 -58.7 -58.4 -58.5 12.1 + {0x1C, 6}, // 1C -15.2 -56.3 -58.3 -60.1 -58.9 -58.9 -58.8 -58.3 -58.4 12.0 + {0x23, 2}, // 23 -15.4 -56.8 -58.3 -60.1 -58.7 -59.0 -58.8 -58.3 -58.5 12.1 + {0x32, 1}, // 32 -15.6 -56.9 -58.8 -59.9 -58.8 -59.0 -58.8 -58.3 -58.5 12.2 + {0x1B, 3}, // 1B -15.9 -56.8 -58.9 -60.0 -58.8 -59.1 -58.7 -58.5 -58.5 11.9 + {0x1A, 7}, // 1A -16.6 -57.3 -59.5 -59.9 -58.8 -59.1 -58.8 -58.4 -58.4 11.8 + {0x19, 8}, // 19 -17.5 -57.5 -59.6 -60.0 -58.7 -59.0 -58.9 -58.3 -58.5 11.7 + {0x18, 10}, // 18 -18.5 -57.8 -60.3 -60.0 -58.8 -59.0 -58.9 -58.2 -58.5 11.6 + {0x22, 3}, // 22 -18.8 -58.4 -60.6 -59.9 -58.7 -59.1 -58.9 -58.5 -58.5 11.7 + {0x0F, 0}, // F -18.8 -58.3 -60.5 -59.8 -58.7 -59.1 -58.9 -58.2 -58.4 11.5 + {0x0E, 5}, // E -19.4 -58.6 -60.9 -59.9 -58.6 -58.9 -58.9 -58.3 -58.5 11.4 + {0x17, 2}, // 17 -19.6 -58.7 -60.9 -60.0 -58.7 -58.9 -58.9 -58.5 -58.4 11.4 + {0x0D, 3}, // D -20.0 -58.8 -61.0 -60.0 -58.8 -59.0 -58.8 -58.5 -58.4 11.4 + {0x0C, 6}, // C -20.7 -59.4 -61.1 -60.0 -58.8 -59.1 -58.9 -58.4 -58.3 11.3 + {0x16, 1}, // 16 -20.9 -59.4 -61.4 -59.9 -58.8 -59.1 -58.9 -58.4 -58.3 11.3 + {0x31, 4}, // 31 -21.3 -59.2 -61.5 -60.0 -58.6 -59.1 -58.9 -58.3 -58.5 11.5 + {0x0B, 0}, // B -21.4 -59.3 -61.8 -60.0 -58.8 -58.9 -58.9 -58.4 -58.5 11.3 + {0x0A, 8}, // A -22.2 -59.9 -61.9 -60.0 -58.9 -59.0 -58.9 -58.4 -58.5 11.2 + {0x15, 1}, // 15 -22.4 -59.9 -62.3 -60.1 -58.6 -59.1 -59.1 -58.3 -58.4 11.2 + {0x09, 6}, // 9 -23.0 -60.3 -62.0 -59.9 -58.7 -59.0 -58.8 -58.4 -58.5 11.1 + {0x08, 10}, // 8 -24.0 -60.5 -62.5 -60.0 -58.7 -59.1 -58.8 -58.3 -58.5 11.1 + {0x14, 3}, // 14 -24.3 -60.6 -62.7 -60.0 -58.9 -59.2 -58.9 -58.2 -58.4 11.1 + {0x21, 1}, // 21 -24.5 -60.7 -62.4 -59.9 -58.9 -58.9 -58.8 -58.3 -58.5 11.2 + {0x07, 6}, // 7 -25.1 -61.3 -62.9 -60.1 -58.8 -59.1 -58.8 -58.4 -58.4 11.0 + {0x06, 12}, // 6 -26.4 -61.6 -63.2 -60.1 -58.7 -59.0 -58.9 -58.5 -58.5 11.0 + {0x13, 2}, // 13 -26.6 -61.8 -63.1 -59.9 -58.7 -59.1 -58.8 -58.4 -58.6 11.0 + {0x05, 10}, // 5 -27.7 -62.3 -63.4 -60.1 -58.7 -59.2 -58.8 -58.4 -58.5 10.9 + {0x04, 19}, // 4 -29.6 -62.7 -63.6 -59.9 -58.7 -59.0 -58.9 -58.4 -58.4 10.8 + {0x12, 1}, // 12 -29.8 -62.8 -63.5 -60.0 -58.7 -59.1 -58.8 -58.3 -58.6 10.9 + {0x03, 18}, // 3 -31.7 -63.4 -63.9 -60.1 -58.8 -59.1 -58.7 -58.3 -58.5 10.8 + {0x02, 29}, // 2 -34.6 -64.0 -64.0 -59.9 -58.7 -58.9 -58.8 -58.4 -58.6 10.7 + {0x11, 0}, // 11 -34.6 -64.0 -64.0 -59.9 -58.7 -59.0 -58.9 -58.3 -58.4 10.8 + {0x01, 36}, // 1 -38.3 -64.9 -63.9 -60.0 -58.9 -58.9 -58.9 -58.4 -58.5 10.7 + {0x10, 29}, // 10 -41.2 -65.3 -64.0 -59.9 -58.6 -59.0 -58.9 -58.4 -58.5 10.6 + {0x30, 0}, // 30 -41.3 -65.5 -63.9 -60.0 -58.7 -59.1 -58.8 -58.4 -58.6 10.9 + {0x20, 0}, // 20 -41.3 -65.3 -64.0 -60.1 -58.8 -59.1 -58.9 -58.4 -58.3 10.8 + {0x00, 225}, // 0 -63.8 -65.4 -64.0 -59.9 -58.9 -58.9 -58.8 -58.3 -58.5 10.3 +}; + +static const PowerTableItem PA_TABLE_433_FULL[] = { + {0xC0, 10}, // C0 9.9 -43.4 -45.0 -53.9 -55.2 -55.8 -52.3 -55.6 29.1 + {0xC1, 4}, // C1 9.5 -45.2 -45.3 -54.6 -55.2 -56.5 -52.5 -56.5 28.3 + {0xC2, 3}, // C2 9.2 -47.1 -45.5 -55.2 -55.1 -56.9 -52.4 -57.1 27.6 + {0xC3, 3}, // C3 8.8 -49.3 -45.9 -55.9 -55.4 -57.2 -52.6 -57.5 26.9 + {0xC4, 3}, // C4 8.5 -51.8 -46.2 -56.3 -55.3 -57.7 -52.9 -57.9 26.3 + {0xC5, 4}, // C5 8.1 -54.6 -46.5 -56.6 -55.4 -58.1 -53.2 -57.9 25.7 + {0xC6, 2}, // C6 7.8 -56.2 -46.9 -56.9 -55.6 -58.2 -53.2 -57.9 25.2 + {0xC7, 3}, // C7 7.4 -57.0 -47.2 -57.0 -55.7 -58.3 -53.5 -57.6 24.7 + {0xC8, 3}, // C8 7.1 -57.4 -47.6 -57.2 -55.9 -58.4 -53.5 -57.2 24.2 + {0xC9, 2}, // C9 6.8 -56.1 -47.9 -57.3 -55.9 -58.5 -54.0 -56.9 23.8 + {0xCA, 3}, // CA 6.4 -54.9 -48.3 -57.2 -56.1 -58.6 -54.2 -56.7 23.4 + {0x80, 1}, // 80 6.3 -50.3 -51.7 -57.7 -53.8 -56.4 -56.3 -57.1 20.6 + {0xCB, 2}, // CB 6.1 -53.8 -48.6 -57.0 -56.1 -58.5 -54.3 -56.7 23.0 + {0x81, 0}, // 81 6.0 -51.4 -52.0 -58.0 -54.0 -56.6 -56.1 -57.4 20.3 + {0xCC, 2}, // CC 5.8 -52.8 -48.9 -57.0 -56.1 -58.4 -54.6 -56.2 22.6 + {0x82, 0}, // 82 5.8 -52.3 -52.1 -58.2 -54.2 -57.0 -56.3 -57.5 20.0 + {0xCD, 2}, // CD 5.5 -52.1 -49.2 -56.9 -56.2 -58.2 -54.8 -56.2 22.3 + {0x83, 0}, // 83 5.5 -53.0 -52.5 -58.3 -54.4 -57.4 -56.4 -57.8 19.7 + {0x84, 4}, // 84 5.1 -53.7 -52.9 -58.1 -54.6 -57.5 -56.6 -57.9 19.4 + {0xCE, 1}, // CE 4.9 -50.6 -49.9 -56.6 -56.3 -58.4 -55.4 -55.8 21.6 + {0x85, 1}, // 85 4.8 -54.2 -53.0 -58.3 -55.0 -57.8 -56.8 -58.0 19.1 + {0x86, 3}, // 86 4.4 -54.9 -53.1 -58.2 -55.1 -57.8 -56.9 -58.1 18.8 + {0x87, 4}, // 87 4.0 -55.6 -53.5 -58.2 -55.3 -58.1 -56.9 -58.2 18.5 + {0x88, 3}, // 88 3.6 -56.2 -53.8 -58.3 -55.7 -58.1 -57.2 -58.2 18.2 + {0x89, 3}, // 89 3.2 -56.8 -53.9 -58.2 -55.8 -58.1 -57.3 -58.0 17.9 + {0x8A, 4}, // 8A 2.8 -57.4 -54.1 -58.2 -56.0 -58.3 -57.7 -58.1 17.6 + {0x8B, 5}, // 8B 2.3 -57.7 -54.5 -58.0 -56.3 -58.1 -57.5 -58.2 17.3 + {0xCF, 2}, // CF 2.0 -48.3 -52.4 -56.0 -57.0 -57.7 -57.3 -56.4 19.3 + {0x8C, 1}, // 8C 1.9 -58.2 -54.7 -58.1 -56.7 -58.3 -58.0 -58.2 17.1 + {0x8D, 5}, // 8D 1.4 -58.4 -54.9 -58.0 -56.8 -58.2 -58.2 -58.1 16.8 + {0x8E, 9}, // 8E 0.4 -58.0 -55.5 -57.8 -57.4 -58.2 -58.1 -58.4 16.2 + {0x50, 0}, // 50 0.4 -59.0 -55.2 -58.2 -57.4 -58.4 -57.8 -58.4 16.0 + {0x60, 3}, // 60 0.1 -59.1 -55.4 -58.1 -57.4 -58.4 -57.7 -58.3 15.9 + {0x51, 4}, // 51 -0.3 -59.2 -55.8 -58.2 -57.5 -58.4 -57.9 -58.3 15.7 + {0x61, 2}, // 61 -0.5 -59.3 -55.8 -58.2 -57.8 -58.3 -58.1 -58.3 15.6 + {0x40, 3}, // 40 -0.8 -59.7 -56.1 -58.2 -57.7 -58.4 -58.3 -58.2 15.4 + {0x52, 0}, // 52 -0.9 -59.6 -56.1 -58.3 -57.7 -58.3 -58.3 -58.3 15.3 + {0x3F, 2}, // 3F -1.1 -61.3 -56.9 -58.2 -57.9 -58.5 -58.2 -58.6 21.1 + {0x62, 0}, // 62 -1.1 -59.6 -56.2 -58.2 -57.8 -58.4 -58.2 -58.3 15.3 + {0x3E, 2}, // 3E -1.4 -61.4 -57.0 -58.3 -57.9 -58.5 -58.2 -58.5 20.5 + {0x53, 1}, // 53 -1.5 -59.7 -56.5 -58.3 -57.9 -58.4 -58.1 -58.3 15.0 + {0x3D, 1}, // 3D -1.7 -61.1 -57.2 -58.3 -57.9 -58.6 -58.3 -58.5 19.9 + {0x63, 0}, // 63 -1.7 -59.8 -56.7 -58.3 -57.9 -58.4 -58.4 -58.3 15.0 + {0x3C, 4}, // 3C -2.1 -60.6 -57.3 -58.2 -58.0 -58.5 -58.4 -58.5 19.3 + {0x54, 1}, // 54 -2.2 -60.0 -56.8 -58.3 -58.1 -58.5 -58.4 -58.5 14.8 + {0x64, 0}, // 64 -2.3 -60.1 -57.0 -58.2 -58.2 -58.5 -58.6 -58.5 14.7 + {0x3B, 2}, // 3B -2.5 -60.4 -57.5 -58.3 -58.1 -58.5 -58.2 -58.5 18.7 + {0x55, 2}, // 55 -2.8 -60.3 -57.2 -58.3 -58.2 -58.3 -58.4 -58.6 14.5 + {0x65, 1}, // 65 -2.9 -60.2 -57.2 -58.3 -58.3 -58.4 -58.4 -58.4 14.5 + {0x2F, 1}, // 2F -3.0 -58.4 -57.6 -58.3 -58.1 -58.6 -58.4 -58.5 17.9 + {0x3A, 1}, // 3A -3.1 -59.5 -57.5 -58.3 -58.3 -58.6 -58.1 -58.6 18.1 + {0x56, 1}, // 56 -3.3 -60.2 -57.5 -58.4 -58.6 -58.5 -58.4 -58.3 14.3 + {0x2E, 2}, // 2E -3.5 -58.0 -57.8 -58.3 -58.4 -58.3 -58.3 -58.5 17.5 + {0x66, 0}, // 66 -3.5 -60.3 -57.6 -58.2 -58.4 -58.3 -58.5 -58.5 14.2 + {0x39, 2}, // 39 -3.8 -58.7 -57.8 -58.3 -58.3 -58.4 -58.4 -58.5 17.5 + {0x57, 2}, // 57 -4.0 -60.3 -57.7 -58.4 -58.4 -58.4 -58.7 -58.4 14.1 + {0x2D, 0}, // 2D -4.0 -57.6 -58.0 -58.2 -58.5 -58.4 -58.6 -58.5 17.1 + {0x67, 0}, // 67 -4.1 -60.1 -57.8 -58.1 -58.6 -58.4 -58.7 -58.6 14.0 + {0x8F, 5}, // 8F -4.6 -52.2 -57.7 -58.1 -58.8 -58.4 -58.7 -58.3 14.4 + {0x2C, 1}, // 2C -4.7 -57.4 -58.3 -58.3 -58.7 -58.5 -58.5 -58.5 16.7 + {0x38, 0}, // 38 -4.7 -58.2 -58.0 -58.3 -58.6 -58.4 -58.5 -58.5 16.9 + {0x68, 0}, // 68 -4.7 -60.4 -57.8 -58.4 -58.7 -58.4 -58.7 -58.5 13.8 + {0x2B, 5}, // 2B -5.3 -57.0 -58.7 -58.4 -58.6 -58.4 -58.5 -58.5 16.3 + {0x69, 0}, // 69 -5.3 -60.6 -58.4 -58.3 -58.8 -58.4 -58.7 -58.5 13.6 + {0x37, 2}, // 37 -5.6 -56.8 -58.3 -58.3 -58.8 -58.4 -58.5 -58.4 16.2 + {0x6A, 3}, // 6A -5.9 -60.8 -58.6 -58.3 -58.7 -58.5 -58.6 -58.5 13.5 + {0x2A, 0}, // 2A -6.0 -56.8 -58.6 -58.4 -58.8 -58.4 -58.6 -58.3 15.9 + {0x6B, 5}, // 6B -6.5 -60.5 -58.9 -58.3 -58.6 -58.5 -58.7 -58.3 13.3 + {0x36, 2}, // 36 -6.8 -56.8 -58.9 -58.3 -58.8 -58.3 -58.5 -58.5 15.6 + {0x29, 1}, // 29 -6.9 -56.7 -58.9 -58.1 -58.6 -58.5 -58.6 -58.5 15.5 + {0x6C, 1}, // 6C -7.1 -60.5 -58.9 -58.2 -58.9 -58.5 -58.6 -58.6 13.2 + {0x6D, 6}, // 6D -7.7 -60.4 -59.2 -58.4 -59.0 -58.5 -58.6 -58.5 13.0 + {0x28, 0}, // 28 -7.8 -56.6 -59.0 -58.2 -59.0 -58.4 -58.5 -58.4 15.1 + {0x35, 5}, // 35 -8.3 -56.8 -59.1 -58.4 -58.9 -58.5 -58.6 -58.5 15.0 + {0x27, 3}, // 27 -8.7 -56.8 -59.2 -58.2 -58.9 -58.5 -58.6 -58.4 14.7 + {0x6E, 2}, // 6E -8.9 -60.2 -59.3 -58.3 -58.8 -58.5 -58.7 -58.5 12.8 + {0x26, 10}, // 26 -9.9 -57.0 -59.4 -58.3 -59.0 -58.4 -58.7 -58.4 14.3 + {0x34, 1}, // 34 -10.1 -57.0 -59.4 -58.4 -58.9 -58.3 -58.7 -58.5 14.4 + {0x25, 13}, // 25 -11.4 -57.3 -59.7 -58.4 -59.0 -58.3 -58.7 -58.5 13.9 + {0x33, 9}, // 33 -12.3 -57.8 -59.8 -58.4 -58.9 -58.3 -58.7 -58.6 13.8 + {0x24, 10}, // 24 -13.3 -57.9 -59.9 -58.2 -59.0 -58.6 -58.7 -58.5 13.5 + {0x1F, 3}, // 1F -13.7 -57.9 -59.9 -58.4 -59.0 -58.5 -58.7 -58.5 13.3 + {0x1E, 6}, // 1E -14.3 -58.4 -59.8 -58.2 -59.0 -58.4 -58.6 -58.6 13.2 + {0x1D, 5}, // 1D -14.9 -58.6 -59.8 -58.3 -59.0 -58.5 -58.7 -58.5 13.1 + {0x6F, 5}, // 6F -15.5 -59.1 -59.9 -58.3 -59.1 -58.5 -58.7 -58.6 12.4 + {0x1C, 0}, // 1C -15.5 -58.6 -59.9 -58.4 -58.8 -58.6 -58.8 -58.5 12.9 + {0x23, 0}, // 23 -15.6 -59.0 -59.9 -58.1 -59.0 -58.4 -58.6 -58.5 13.1 + {0x32, 0}, // 32 -15.7 -59.0 -59.8 -58.2 -58.8 -58.5 -58.6 -58.5 13.1 + {0x1B, 5}, // 1B -16.2 -59.1 -59.9 -58.3 -59.0 -58.5 -58.6 -58.4 12.8 + {0x1A, 8}, // 1A -17.0 -59.4 -59.9 -58.3 -59.1 -58.5 -58.7 -58.4 12.7 + {0x19, 8}, // 19 -17.8 -59.7 -59.9 -58.3 -59.0 -58.4 -58.6 -58.5 12.6 + {0x18, 10}, // 18 -18.8 -60.2 -59.9 -58.2 -59.0 -58.5 -58.7 -58.6 12.5 + {0x22, 1}, // 22 -19.0 -60.4 -59.9 -58.3 -59.0 -58.5 -58.6 -58.5 12.6 + {0x0F, 3}, // F -19.3 -60.4 -59.8 -58.4 -59.0 -58.6 -58.6 -58.5 12.4 + {0x17, 5}, // 17 -19.8 -60.6 -59.9 -58.2 -58.9 -58.4 -58.7 -58.4 12.4 + {0x0E, 0}, // E -19.8 -60.5 -59.9 -58.3 -59.1 -58.5 -58.8 -58.6 12.4 + {0x0D, 5}, // D -20.4 -60.7 -60.0 -58.4 -59.0 -58.5 -58.5 -58.5 12.3 + {0x0C, 6}, // C -21.0 -61.1 -59.9 -58.4 -59.0 -58.5 -58.7 -58.6 12.3 + {0x16, 0}, // 16 -21.0 -61.2 -59.9 -58.3 -59.1 -58.5 -58.7 -58.4 12.3 + {0x31, 3}, // 31 -21.3 -61.3 -60.0 -58.3 -59.0 -58.5 -58.7 -58.5 12.5 + {0x0B, 3}, // B -21.7 -61.3 -59.9 -58.2 -58.9 -58.5 -58.8 -58.4 12.2 + {0x15, 8}, // 15 -22.5 -61.7 -60.0 -58.2 -59.1 -58.3 -58.6 -58.7 12.2 + {0x0A, 0}, // A -22.5 -61.8 -60.0 -58.2 -58.9 -58.6 -58.6 -58.6 12.2 + {0x09, 8}, // 9 -23.3 -61.8 -60.0 -58.2 -59.0 -58.6 -58.6 -58.4 12.1 + {0x08, 10}, // 8 -24.3 -62.3 -59.9 -58.3 -59.0 -58.4 -58.8 -58.5 12.1 + {0x14, 0}, // 14 -24.3 -62.1 -59.9 -58.4 -59.1 -58.5 -58.7 -58.5 12.1 + {0x21, 1}, // 21 -24.5 -62.4 -59.9 -58.3 -59.1 -58.4 -58.6 -58.4 12.2 + {0x07, 8}, // 7 -25.3 -62.6 -59.9 -58.2 -59.0 -58.6 -58.7 -58.5 12.0 + {0x06, 11}, // 6 -26.5 -63.2 -59.9 -58.3 -58.9 -58.5 -58.6 -58.6 12.0 + {0x13, 0}, // 13 -26.5 -63.0 -59.8 -58.3 -59.0 -58.4 -58.7 -58.4 12.0 + {0x05, 13}, // 5 -27.9 -63.5 -59.8 -58.3 -59.1 -58.5 -58.7 -58.4 11.9 + {0x04, 16}, // 4 -29.5 -63.7 -59.9 -58.3 -58.9 -58.5 -58.5 -58.5 11.9 + {0x12, 1}, // 12 -29.6 -63.6 -59.7 -58.3 -59.1 -58.6 -58.6 -58.5 11.9 + {0x03, 17}, // 3 -31.4 -64.2 -60.1 -58.4 -59.0 -58.6 -58.8 -58.6 11.8 + {0x11, 23}, // 11 -33.8 -64.3 -59.9 -58.3 -58.9 -58.6 -58.6 -58.5 11.8 + {0x02, 0}, // 2 -33.8 -64.4 -59.9 -58.3 -59.1 -58.4 -58.8 -58.6 11.7 + {0x01, 27}, // 1 -36.5 -64.2 -59.9 -58.4 -59.0 -58.6 -58.6 -58.5 11.7 + {0x10, 17}, // 10 -38.3 -64.4 -59.9 -58.4 -59.0 -58.5 -58.8 -58.5 11.7 + {0x20, 0}, // 20 -38.3 -64.6 -59.9 -58.3 -59.0 -58.6 -58.7 -58.5 11.8 + {0x30, 1}, // 30 -38.4 -64.4 -59.9 -58.3 -59.1 -58.5 -58.7 -58.6 11.9 + {0x00, 243}, // 0 -62.7 -64.5 -59.8 -58.2 -58.9 -58.5 -58.7 -58.6 11.3 +}; + +static const PowerTableItem PA_TABLE_868_FULL[] = { + {0xC0, 3}, // C0 10.7 -35.1 -58.6 -58.6 -57.5 -50.0 34.2 + {0xC1, 3}, // C1 10.3 -37.0 -58.5 -58.6 -57.5 -51.4 33.3 + {0xC2, 3}, // C2 10.0 -39.2 -58.3 -58.5 -57.4 -53.2 32.4 + {0xC3, 4}, // C3 9.6 -41.5 -58.5 -58.3 -57.4 -54.4 31.6 + {0xC4, 4}, // C4 9.2 -44.0 -58.6 -58.5 -57.4 -55.2 30.9 + {0xC5, 2}, // C5 8.9 -46.2 -58.5 -58.6 -57.6 -55.4 30.2 + {0xC6, 4}, // C6 8.5 -47.7 -58.5 -58.3 -57.6 -55.0 29.5 + {0xC7, 3}, // C7 8.2 -47.3 -58.5 -58.5 -57.7 -54.5 28.9 + {0xC8, 3}, // C8 7.8 -45.9 -58.5 -58.4 -57.7 -54.1 28.3 + {0xC9, 2}, // C9 7.5 -44.4 -58.5 -58.5 -57.7 -53.6 27.8 + {0xCA, 2}, // CA 7.2 -42.9 -58.6 -58.4 -57.6 -53.2 27.3 + {0xCB, 4}, // CB 6.8 -41.7 -58.5 -58.4 -57.8 -52.8 26.8 + {0xCC, 2}, // CC 6.5 -40.6 -58.6 -58.4 -57.6 -52.5 26.3 + {0xCD, 2}, // CD 6.2 -39.8 -58.4 -58.6 -57.6 -52.3 25.9 + {0xCE, 7}, // CE 5.5 -38.5 -58.5 -58.4 -57.8 -52.2 25.0 + {0x80, 2}, // 80 5.2 -31.9 -58.5 -58.4 -57.8 -56.2 21.2 + {0x81, 2}, // 81 5.0 -32.6 -58.5 -58.6 -57.8 -56.2 21.0 + {0x82, 2}, // 82 4.8 -33.4 -58.4 -58.4 -57.7 -56.2 20.8 + {0x83, 2}, // 83 4.6 -34.3 -58.4 -58.5 -57.7 -56.1 20.5 + {0x84, 1}, // 84 4.4 -35.3 -58.7 -58.5 -57.8 -55.8 20.3 + {0x85, 3}, // 85 4.1 -36.5 -58.7 -58.6 -57.7 -55.8 20.0 + {0x86, 3}, // 86 3.7 -37.8 -58.6 -58.5 -57.8 -55.8 19.7 + {0x87, 3}, // 87 3.4 -39.4 -58.6 -58.6 -57.8 -55.7 19.5 + {0x88, 3}, // 88 3.0 -41.0 -58.5 -58.5 -57.7 -55.7 19.1 + {0x89, 3}, // 89 2.6 -43.0 -58.5 -58.6 -57.6 -55.8 18.8 + {0xCF, 2}, // CF 2.4 -36.6 -58.6 -58.4 -57.7 -53.6 22.0 + {0x8A, 2}, // 8A 2.1 -45.1 -58.5 -58.5 -57.6 -55.8 18.5 + {0x8B, 4}, // 8B 1.7 -47.5 -58.5 -58.4 -57.8 -55.8 18.2 + {0x8C, 5}, // 8C 1.1 -50.2 -58.6 -58.5 -57.7 -55.9 17.9 + {0x8D, 5}, // 8D 0.6 -52.6 -58.5 -58.5 -57.8 -55.8 17.6 + {0x50, 9}, // 50 -0.3 -42.1 -58.5 -58.5 -57.6 -57.1 16.9 + {0x60, 2}, // 60 -0.5 -42.3 -58.6 -58.5 -57.6 -57.2 16.8 + {0x8E, 0}, // 8E -0.5 -55.5 -58.5 -58.6 -57.7 -56.2 17.0 + {0x51, 4}, // 51 -0.9 -42.6 -58.6 -58.6 -57.6 -57.1 16.5 + {0x61, 2}, // 61 -1.1 -42.8 -58.3 -58.5 -57.6 -57.1 16.4 + {0x40, 3}, // 40 -1.5 -43.2 -58.5 -58.7 -57.7 -57.2 16.1 + {0x52, 1}, // 52 -1.6 -43.2 -58.5 -58.5 -57.7 -57.2 16.1 + {0x62, 1}, // 62 -1.8 -43.2 -58.4 -58.6 -57.7 -57.0 16.0 + {0x53, 4}, // 53 -2.3 -43.7 -58.6 -58.6 -57.7 -57.2 15.7 + {0x63, 1}, // 63 -2.4 -43.8 -58.6 -58.4 -57.6 -57.1 15.6 + {0x3F, 2}, // 3F -2.6 -53.7 -58.6 -58.5 -57.8 -57.5 21.4 + {0x3E, 1}, // 3E -2.8 -54.2 -58.6 -58.5 -57.7 -57.5 20.8 + {0x54, 1}, // 54 -2.9 -44.4 -58.5 -58.6 -57.8 -57.4 15.3 + {0x64, 2}, // 64 -3.1 -44.3 -58.6 -58.6 -57.7 -57.4 15.3 + {0x3D, 1}, // 3D -3.2 -54.7 -58.6 -58.5 -57.7 -57.5 20.2 + {0x3C, 2}, // 3C -3.5 -55.0 -58.6 -58.5 -57.7 -57.5 19.6 + {0x55, 1}, // 55 -3.6 -44.9 -58.6 -58.4 -57.8 -57.5 15.0 + {0x65, 1}, // 65 -3.7 -44.9 -58.6 -58.5 -57.7 -57.5 15.0 + {0x3B, 2}, // 3B -4.0 -55.0 -58.5 -58.5 -57.7 -57.3 19.0 + {0x56, 2}, // 56 -4.2 -45.4 -58.5 -58.5 -57.7 -57.5 14.7 + {0x66, 2}, // 66 -4.4 -45.4 -58.5 -58.5 -57.7 -57.4 14.7 + {0x2F, 0}, // 2F -4.5 -52.6 -58.6 -58.6 -57.6 -57.4 18.2 + {0x3A, 0}, // 3A -4.5 -54.1 -58.6 -58.5 -57.8 -57.5 18.4 + {0x57, 2}, // 57 -4.8 -46.0 -58.6 -58.5 -57.6 -57.4 14.5 + {0x2E, 1}, // 2E -4.9 -51.8 -58.6 -58.4 -57.7 -57.4 17.8 + {0x67, 0}, // 67 -5.0 -46.0 -58.5 -58.5 -57.8 -57.5 14.4 + {0x39, 2}, // 39 -5.2 -52.9 -58.6 -58.6 -57.7 -57.5 17.8 + {0x2D, 2}, // 2D -5.5 -50.9 -58.6 -58.5 -57.7 -57.4 17.4 + {0x68, 2}, // 68 -5.7 -46.6 -58.5 -58.5 -57.7 -57.5 14.2 + {0x8F, 2}, // 8F -6.0 -51.6 -58.5 -58.6 -57.7 -57.1 15.0 + {0x2C, 0}, // 2C -6.0 -50.3 -58.6 -58.5 -57.7 -57.3 17.0 + {0x38, 0}, // 38 -6.1 -51.8 -58.6 -58.5 -57.7 -57.3 17.1 + {0x69, 2}, // 69 -6.3 -47.3 -58.4 -58.6 -57.6 -57.5 14.0 + {0x2B, 4}, // 2B -6.7 -49.8 -58.5 -58.5 -57.7 -57.5 16.6 + {0x6A, 2}, // 6A -6.9 -48.1 -58.6 -58.5 -57.7 -57.5 13.8 + {0x37, 0}, // 37 -6.9 -49.5 -58.5 -58.5 -57.6 -57.3 16.4 + {0x2A, 5}, // 2A -7.4 -49.3 -58.5 -58.6 -57.7 -57.4 16.2 + {0x6B, 0}, // 6B -7.5 -49.5 -58.5 -58.6 -57.7 -57.4 13.8 + {0x36, 5}, // 36 -8.1 -49.0 -58.5 -58.5 -57.7 -57.3 15.8 + {0x29, 0}, // 29 -8.2 -49.1 -58.6 -58.5 -57.8 -57.4 15.8 + {0x6C, 5}, // 6C -8.7 -52.2 -58.6 -58.5 -57.6 -57.4 14.0 + {0x28, 3}, // 28 -9.0 -49.0 -58.5 -58.6 -57.7 -57.4 15.4 + {0x35, 4}, // 35 -9.4 -48.9 -58.5 -58.5 -57.8 -57.3 15.2 + {0x27, 4}, // 27 -9.8 -49.0 -58.5 -58.5 -57.7 -57.3 15.0 + {0x26, 11}, // 26 -11.0 -49.2 -58.5 -58.5 -57.7 -57.4 14.6 + {0x34, 0}, // 34 -11.1 -49.2 -58.6 -58.6 -57.7 -57.3 14.6 + {0x25, 14}, // 25 -12.5 -49.5 -58.6 -58.6 -57.8 -57.3 14.1 + {0x33, 8}, // 33 -13.3 -49.7 -58.6 -58.5 -57.8 -57.5 14.0 + {0x24, 10}, // 24 -14.3 -50.2 -58.5 -58.4 -57.8 -57.4 13.7 + {0x6D, 1}, // 6D -14.5 -55.3 -58.6 -58.5 -57.7 -57.5 14.7 + {0x1F, 0}, // 1F -14.6 -50.3 -58.6 -58.6 -57.7 -57.5 13.5 + {0x1E, 5}, // 1E -15.1 -50.6 -58.6 -58.5 -57.6 -57.4 13.4 + {0x1D, 5}, // 1D -15.7 -50.7 -58.6 -58.6 -57.8 -57.5 13.3 + {0x1C, 6}, // 1C -16.4 -51.1 -58.6 -58.4 -57.6 -57.3 13.2 + {0x23, 1}, // 23 -16.5 -51.0 -58.6 -58.6 -57.8 -57.4 13.3 + {0x32, 0}, // 32 -16.5 -51.0 -58.6 -58.5 -57.7 -57.4 13.4 + {0x1B, 5}, // 1B -17.0 -51.3 -58.5 -58.4 -57.7 -57.5 13.1 + {0x1A, 8}, // 1A -17.8 -51.6 -58.6 -58.5 -57.7 -57.3 13.0 + {0x19, 8}, // 19 -18.6 -52.0 -58.6 -58.5 -57.8 -57.5 12.9 + {0x18, 8}, // 18 -19.5 -52.6 -58.5 -58.5 -57.8 -57.4 12.8 + {0x22, 1}, // 22 -19.6 -52.5 -58.5 -58.6 -57.7 -57.4 12.9 + {0x0F, 3}, // F -20.0 -52.8 -58.5 -58.5 -57.7 -57.6 12.7 + {0x0E, 5}, // E -20.5 -53.0 -58.5 -58.6 -57.7 -57.6 12.7 + {0x17, 0}, // 17 -20.5 -52.9 -58.6 -58.6 -57.7 -57.5 12.7 + {0x0D, 6}, // D -21.1 -53.3 -58.6 -58.6 -57.8 -57.4 12.6 + {0x0C, 5}, // C -21.7 -53.6 -58.6 -58.5 -57.7 -57.5 12.6 + {0x16, 0}, // 16 -21.7 -53.6 -58.5 -58.6 -57.6 -57.5 12.6 + {0x31, 1}, // 31 -21.9 -53.7 -58.5 -58.4 -57.8 -57.3 12.8 + {0x0B, 4}, // B -22.3 -53.9 -58.6 -58.5 -57.8 -57.4 12.5 + {0x0A, 6}, // A -23.0 -54.3 -58.6 -58.5 -57.8 -57.4 12.5 + {0x15, 0}, // 15 -23.0 -54.3 -58.5 -58.5 -57.8 -57.4 12.5 + {0x09, 8}, // 9 -23.8 -54.7 -58.5 -58.5 -57.8 -57.5 12.4 + {0x08, 8}, // 8 -24.6 -55.0 -58.6 -58.6 -57.7 -57.6 12.3 + {0x14, 0}, // 14 -24.7 -55.0 -58.5 -58.5 -57.7 -57.5 12.4 + {0x21, 1}, // 21 -24.8 -55.1 -58.5 -58.5 -57.7 -57.5 12.5 + {0x07, 6}, // 7 -25.5 -55.4 -58.6 -58.5 -57.7 -57.5 12.3 + {0x13, 10}, // 13 -26.5 -55.9 -58.6 -58.5 -57.6 -57.6 12.3 + {0x06, 0}, // 6 -26.5 -55.9 -58.5 -58.5 -57.6 -57.5 12.2 + {0x05, 11}, // 5 -27.7 -56.4 -58.4 -58.4 -57.7 -57.5 12.2 + {0x12, 11}, // 12 -28.9 -57.1 -58.4 -58.5 -57.7 -57.3 12.2 + {0x04, 0}, // 4 -28.9 -57.0 -58.6 -58.4 -57.7 -57.5 12.1 + {0x03, 13}, // 3 -30.2 -57.3 -58.5 -58.5 -57.7 -57.5 12.1 + {0x02, 15}, // 2 -31.7 -57.9 -58.5 -58.5 -57.7 -57.3 12.0 + {0x11, 0}, // 11 -31.7 -58.0 -58.5 -58.5 -57.8 -57.3 12.1 + {0x01, 14}, // 1 -33.1 -58.4 -58.6 -58.5 -57.8 -57.5 12.0 + {0x10, 10}, // 10 -34.1 -58.2 -58.7 -58.3 -57.7 -57.5 11.9 + {0x20, 0}, // 20 -34.1 -58.2 -58.5 -58.5 -57.7 -57.5 12.1 + {0x30, 1}, // 30 -34.2 -58.4 -58.6 -58.5 -57.8 -57.5 12.2 + {0x6E, 115}, // 6E -45.8 -57.9 -58.6 -58.5 -57.7 -57.5 16.1 + {0x00, 135}, // 0 -59.3 -58.3 -58.4 -58.5 -57.8 -57.4 11.3 + {0x6F, 99}, // 6F -69.2 -58.2 -58.5 -58.4 -57.6 -57.4 10.8 +}; + +static const PowerTableItem PA_TABLE_915_FULL[] = { + {0xC0, 15}, // C0 9.4 -33.5 -58.5 -58.4 -55.8 -32.6 31.8 + {0xC1, 4}, // C1 9.0 -35.9 -58.3 -58.5 -56.1 -34.4 30.9 + {0xC2, 4}, // C2 8.6 -38.6 -58.5 -58.4 -56.1 -36.2 30.0 + {0xC3, 2}, // C3 8.3 -41.5 -58.6 -58.4 -56.3 -38.0 29.3 + {0xC4, 4}, // C4 7.9 -43.6 -58.5 -58.4 -56.5 -39.6 28.6 + {0xC5, 3}, // C5 7.6 -43.9 -58.5 -58.5 -56.7 -40.5 28.0 + {0xC6, 3}, // C6 7.2 -42.5 -58.5 -58.4 -56.7 -40.5 27.4 + {0xC7, 2}, // C7 6.9 -40.7 -58.5 -58.5 -56.8 -40.0 26.9 + {0xC8, 3}, // C8 6.6 -39.0 -58.5 -58.4 -56.9 -39.4 26.4 + {0xC9, 3}, // C9 6.2 -37.6 -58.6 -58.4 -57.2 -38.8 25.9 + {0xCA, 2}, // CA 5.9 -36.5 -58.6 -58.4 -57.3 -38.2 25.5 + {0xCB, 3}, // CB 5.6 -35.6 -58.4 -58.4 -57.3 -37.8 25.1 + {0xCC, 2}, // CC 5.3 -34.8 -58.6 -58.3 -57.3 -37.5 24.7 + {0xCD, 2}, // CD 5.0 -34.2 -58.6 -58.5 -57.5 -37.3 24.3 + {0x80, 0}, // 80 4.9 -27.8 -58.5 -58.4 -57.7 -40.6 20.7 + {0x81, 2}, // 81 4.7 -28.7 -58.4 -58.5 -57.7 -40.6 20.5 + {0x82, 2}, // 82 4.5 -29.6 -58.5 -58.4 -57.7 -40.5 20.2 + {0xCE, 2}, // CE 4.3 -33.2 -58.5 -58.3 -57.5 -37.2 23.7 + {0x83, 0}, // 83 4.2 -30.7 -58.5 -58.4 -57.7 -40.4 20.0 + {0x84, 3}, // 84 3.9 -32.0 -58.6 -58.4 -57.7 -40.1 19.7 + {0x85, 2}, // 85 3.6 -33.3 -58.5 -58.5 -57.8 -39.9 19.4 + {0x86, 3}, // 86 3.3 -34.8 -58.5 -58.5 -57.6 -39.8 19.2 + {0x87, 3}, // 87 2.9 -36.5 -58.4 -58.5 -57.7 -39.6 18.9 + {0x88, 3}, // 88 2.5 -38.3 -58.6 -58.4 -57.8 -39.5 18.6 + {0x89, 2}, // 89 2.2 -40.2 -58.6 -58.5 -57.6 -39.6 18.3 + {0x8A, 4}, // 8A 1.8 -42.2 -58.5 -58.4 -57.7 -39.6 18.1 + {0xCF, 1}, // CF 1.6 -31.3 -58.7 -58.3 -57.6 -40.0 21.4 + {0x8B, 3}, // 8B 1.3 -44.0 -58.5 -58.4 -57.7 -39.8 17.8 + {0x8C, 4}, // 8C 0.9 -45.8 -58.4 -58.4 -57.6 -40.0 17.5 + {0x8D, 4}, // 8D 0.5 -46.8 -58.5 -58.5 -57.7 -40.4 17.3 + {0x8E, 11}, // 8E -0.6 -46.6 -58.5 -58.5 -57.8 -41.1 16.7 + {0x50, 3}, // 50 -0.9 -37.9 -58.5 -58.5 -57.8 -45.9 16.5 + {0x60, 2}, // 60 -1.1 -38.0 -58.4 -58.4 -57.7 -46.1 16.4 + {0x51, 5}, // 51 -1.6 -38.7 -58.4 -58.5 -57.7 -46.9 16.0 + {0x61, 1}, // 61 -1.8 -38.7 -58.5 -58.4 -57.7 -47.0 15.9 + {0x40, 3}, // 40 -2.1 -39.4 -58.6 -58.5 -57.7 -47.5 15.7 + {0x52, 1}, // 52 -2.2 -39.3 -58.5 -58.4 -57.7 -47.4 15.6 + {0x62, 1}, // 62 -2.4 -39.4 -58.5 -58.5 -57.8 -47.6 15.6 + {0x3F, 1}, // 3F -2.5 -49.6 -58.4 -58.4 -57.6 -55.1 21.3 + {0x3E, 2}, // 3E -2.7 -50.0 -58.5 -58.4 -57.6 -55.3 20.7 + {0x53, 1}, // 53 -2.9 -40.1 -58.5 -58.4 -57.8 -47.7 15.3 + {0x3D, 1}, // 3D -3.0 -50.3 -58.4 -58.5 -57.7 -55.2 20.1 + {0x63, 0}, // 63 -3.0 -40.1 -58.4 -58.4 -57.8 -47.7 15.2 + {0x3C, 3}, // 3C -3.4 -50.7 -58.4 -58.5 -57.7 -55.3 19.5 + {0x54, 1}, // 54 -3.5 -40.8 -58.5 -58.5 -57.7 -48.0 15.0 + {0x64, 2}, // 64 -3.7 -40.8 -58.5 -58.5 -57.6 -48.1 14.9 + {0x3B, 0}, // 3B -3.8 -50.7 -58.6 -58.4 -57.6 -55.2 18.9 + {0x55, 2}, // 55 -4.1 -41.6 -58.5 -58.4 -57.7 -49.2 14.7 + {0x2F, 1}, // 2F -4.2 -50.3 -58.5 -58.4 -57.7 -54.1 18.1 + {0x65, 0}, // 65 -4.3 -41.6 -58.6 -58.4 -57.6 -49.2 14.6 + {0x3A, 1}, // 3A -4.4 -50.5 -58.6 -58.4 -57.7 -54.8 18.3 + {0x2E, 2}, // 2E -4.7 -49.4 -58.5 -58.4 -57.7 -53.6 17.7 + {0x56, 0}, // 56 -4.7 -42.3 -58.5 -58.5 -57.8 -50.5 14.4 + {0x66, 2}, // 66 -4.9 -42.3 -58.5 -58.4 -57.7 -50.6 14.4 + {0x39, 1}, // 39 -5.1 -50.0 -58.5 -58.5 -57.6 -54.0 17.7 + {0x2D, 1}, // 2D -5.2 -48.8 -58.5 -58.4 -57.7 -53.0 17.3 + {0x57, 2}, // 57 -5.4 -43.1 -58.6 -58.4 -57.8 -51.9 14.2 + {0x67, 1}, // 67 -5.6 -43.1 -58.5 -58.4 -57.8 -52.1 14.1 + {0x8F, 1}, // 8F -5.7 -46.0 -58.6 -58.4 -57.6 -45.5 14.9 + {0x2C, 0}, // 2C -5.8 -48.1 -58.5 -58.4 -57.7 -52.4 16.9 + {0x38, 1}, // 38 -5.9 -49.2 -58.6 -58.4 -57.7 -53.3 17.1 + {0x68, 2}, // 68 -6.2 -43.9 -58.5 -58.5 -57.6 -51.6 13.9 + {0x2B, 2}, // 2B -6.4 -47.6 -58.4 -58.4 -57.8 -52.1 16.5 + {0x37, 2}, // 37 -6.7 -47.4 -58.6 -58.4 -57.7 -51.7 16.4 + {0x69, 0}, // 69 -6.8 -44.9 -58.5 -58.4 -57.7 -51.8 13.8 + {0x2A, 4}, // 2A -7.2 -47.2 -58.6 -58.4 -57.8 -51.6 16.1 + {0x6A, 2}, // 6A -7.4 -46.2 -58.5 -58.4 -57.9 -53.1 13.7 + {0x36, 5}, // 36 -7.9 -46.9 -58.5 -58.4 -57.7 -51.2 15.8 + {0x29, 0}, // 29 -8.0 -47.0 -58.6 -58.4 -57.8 -51.2 15.7 + {0x6B, 4}, // 6B -8.4 -49.0 -58.5 -58.4 -57.8 -55.4 13.9 + {0x28, 4}, // 28 -8.8 -46.8 -58.4 -58.5 -57.6 -50.9 15.3 + {0x35, 5}, // 35 -9.3 -46.7 -58.6 -58.4 -57.7 -50.7 15.2 + {0x27, 3}, // 27 -9.7 -46.7 -58.5 -58.4 -57.6 -50.7 14.9 + {0x26, 12}, // 26 -10.9 -47.0 -58.6 -58.4 -57.8 -50.9 14.5 + {0x34, 0}, // 34 -10.9 -47.0 -58.5 -58.4 -57.8 -50.8 14.5 + {0x25, 14}, // 25 -12.3 -47.2 -58.6 -58.3 -57.7 -51.0 14.1 + {0x6C, 0}, // 6C -12.4 -54.4 -58.6 -58.4 -57.8 -55.3 14.6 + {0x33, 6}, // 33 -13.1 -47.6 -58.5 -58.4 -57.8 -51.2 14.0 + {0x24, 10}, // 24 -14.1 -48.1 -58.4 -58.4 -57.8 -51.4 13.7 + {0x1F, 3}, // 1F -14.4 -48.2 -58.5 -58.4 -57.7 -51.5 13.5 + {0x1E, 5}, // 1E -14.9 -48.4 -58.5 -58.4 -57.8 -51.7 13.4 + {0x1D, 5}, // 1D -15.5 -48.7 -58.4 -58.5 -57.7 -51.9 13.2 + {0x1C, 6}, // 1C -16.1 -49.0 -58.4 -58.4 -57.7 -52.2 13.1 + {0x23, 1}, // 23 -16.3 -49.1 -58.4 -58.5 -57.8 -51.9 13.3 + {0x32, 0}, // 32 -16.4 -49.0 -58.5 -58.5 -57.7 -52.0 13.3 + {0x1B, 4}, // 1B -16.8 -49.3 -58.6 -58.4 -57.8 -52.3 13.0 + {0x1A, 6}, // 1A -17.5 -49.7 -58.5 -58.4 -57.8 -52.6 12.9 + {0x19, 8}, // 19 -18.3 -50.2 -58.5 -58.5 -57.6 -52.8 12.8 + {0x18, 10}, // 18 -19.3 -50.6 -58.5 -58.5 -57.7 -53.1 12.7 + {0x22, 0}, // 22 -19.4 -50.7 -58.5 -58.5 -57.8 -53.1 12.8 + {0x0F, 3}, // F -19.7 -50.9 -58.5 -58.5 -57.7 -52.9 12.6 + {0x0E, 5}, // E -20.2 -51.2 -58.6 -58.5 -57.8 -53.3 12.6 + {0x17, 1}, // 17 -20.3 -51.2 -58.6 -58.5 -57.8 -53.1 12.6 + {0x0D, 5}, // D -20.8 -51.4 -58.5 -58.5 -57.8 -53.2 12.5 + {0x0C, 5}, // C -21.4 -51.8 -58.4 -58.5 -57.7 -53.4 12.5 + {0x16, 0}, // 16 -21.4 -51.9 -58.5 -58.5 -57.7 -53.4 12.5 + {0x31, 3}, // 31 -21.7 -52.0 -58.5 -58.4 -57.8 -53.4 12.7 + {0x0B, 3}, // B -22.0 -52.2 -58.5 -58.4 -57.9 -53.6 12.4 + {0x0A, 6}, // A -22.7 -52.6 -58.5 -58.4 -57.7 -53.6 12.4 + {0x15, 1}, // 15 -22.8 -52.7 -58.5 -58.4 -57.8 -53.6 12.4 + {0x09, 6}, // 9 -23.5 -53.1 -58.5 -58.5 -57.7 -54.0 12.3 + {0x6D, 3}, // 6D -23.8 -50.2 -58.5 -58.4 -57.6 -55.3 15.5 + {0x08, 5}, // 8 -24.3 -53.6 -58.4 -58.4 -57.6 -54.1 12.3 + {0x14, 0}, // 14 -24.4 -53.7 -58.6 -58.4 -57.8 -53.9 12.3 + {0x21, 2}, // 21 -24.6 -53.6 -58.6 -58.4 -57.8 -53.9 12.4 + {0x07, 5}, // 7 -25.2 -54.1 -58.6 -58.4 -57.7 -54.0 12.2 + {0x13, 10}, // 13 -26.2 -54.6 -58.4 -58.5 -57.7 -54.3 12.2 + {0x06, 0}, // 6 -26.2 -54.6 -58.5 -58.3 -57.7 -54.3 12.2 + {0x05, 11}, // 5 -27.3 -55.3 -58.4 -58.4 -57.8 -54.5 12.1 + {0x12, 13}, // 12 -28.6 -55.9 -58.6 -58.5 -57.7 -54.7 12.1 + {0x04, 0}, // 4 -28.6 -56.1 -58.5 -58.4 -57.8 -54.5 12.0 + {0x03, 11}, // 3 -29.8 -56.9 -58.5 -58.4 -57.7 -54.7 12.0 + {0x02, 13}, // 2 -31.2 -57.5 -58.4 -58.4 -57.9 -54.7 11.9 + {0x11, 1}, // 11 -31.3 -57.6 -58.6 -58.3 -57.8 -54.7 12.0 + {0x01, 14}, // 1 -32.7 -58.4 -58.4 -58.4 -57.7 -54.7 11.9 + {0x10, 8}, // 10 -33.6 -58.6 -58.5 -58.3 -57.8 -54.8 11.9 + {0x20, 1}, // 20 -33.7 -58.6 -58.5 -58.3 -57.9 -54.6 12.0 + {0x30, 0}, // 30 -33.7 -58.5 -58.5 -58.4 -57.7 -54.9 12.1 + {0x00, 245}, // 0 -58.2 -58.5 -58.6 -58.3 -57.6 -54.1 11.2 + {0x6E, 62}, // 6E -64.5 -58.8 -58.5 -58.4 -57.7 -55.5 16.0 + {0x6F, 52}, // 6F -69.7 -58.6 -58.6 -58.4 -57.7 -55.3 10.7 +}; +*/ +} // namespace cc1101 +} // namespace esphome diff --git a/components/cc1101/cc1101sub.h b/components/cc1101/cc1101sub.h new file mode 100644 index 0000000..5012f3c --- /dev/null +++ b/components/cc1101/cc1101sub.h @@ -0,0 +1,57 @@ +#pragma once + +namespace esphome { +namespace cc1101 { + +#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 + +#define CC1101_SUB_NUMBER(name, type) \ + SUB_NUMBER(name) \ + void publish_##name() { this->publish_(this->name##_number_, (float) this->get_##name()); } \ + void set_##name(type value); \ + type get_##name(); + +#define CC1101_SUB_SWITCH(name) \ + SUB_SWITCH(name) \ + void publish_##name() { this->publish_switch_(this->name##_switch_, this->get_##name()); } \ + void set_##name(bool value); \ + bool get_##name(); + +#define CC1101_SUB_SELECT(name, type) \ + SUB_SELECT(name) \ + void publish_##name() { this->publish_select_(this->name##_select_, (size_t) this->get_##name()); } \ + void set_##name(type value); \ + type get_##name(); + +#define CC1101_SUB_TEXT(name) \ + SUB_TEXT(name) \ + void publish_##name() { this->publish_(this->name##_text_, this->get_##name()); } \ + void set_##name(const std::string &value); \ + std::string get_##name(); + +#define CC1101_SUB_SENSOR(name) \ + SUB_SENSOR(name) \ + void publish_##name##_sensor() { this->publish_(this->name##_sensor_, (float) this->get_##name##_sensor()); } \ + float get_##name##_sensor(); + +#define CC1101_SUB_BINARY_SENSOR(name) \ + SUB_BINARY_SENSOR(name) \ + void publish_##name##_binary_sensor() { \ + this->publish_(this->name##_binary_sensor_, this->get_##name##_binary_sensor()); \ + } \ + bool get_##name##_binary_sensor(); + +#define CC1101_SUB_TEXT_SENSOR(name) \ + SUB_TEXT_SENSOR(name) \ + void publish_##name##_text_sensor() { this->publish_(this->name##_text_sensor_, this->get_##name##_text_sensor()); } \ + std::string get_##name##_text_sensor(); + +} // namespace cc1101 +} // namespace esphome diff --git a/components/cc1101/number/__init__.py b/components/cc1101/number/__init__.py new file mode 100644 index 0000000..beaf838 --- /dev/null +++ b/components/cc1101/number/__init__.py @@ -0,0 +1,204 @@ +import esphome.codegen as cg +from esphome.components import number +import esphome.config_validation as cv +from esphome.const import ( + CONF_CHANNEL, + CONF_FREQUENCY, + CONF_MODE, + DEVICE_CLASS_FREQUENCY, + DEVICE_CLASS_SIGNAL_STRENGTH, + ENTITY_CATEGORY_CONFIG, + UNIT_DECIBEL, + UNIT_DECIBEL_MILLIWATT, +) + +from .. import ( + CC1101_COMPONENT_SCHEMA, + CONF_AGC, + CONF_BANDWIDTH, + CONF_CARRIER_SENSE_ABS_THR, + CONF_CC1101_ID, + CONF_CHANNEL_SPACING, + CONF_FSK_DEVIATION, + CONF_IF_FREQUENCY, + CONF_MSK_DEVIATION, + CONF_OUTPUT_POWER, + CONF_SYMBOL_RATE, + CONF_TUNER, + UNIT_KILO_HERTZ, + for_each_conf, + ns, +) + +NumberMode = number.NumberMode + +OutputPowerNumber = ns.class_("OutputPowerNumber", number.Number) +TunerFrequencyNumber = ns.class_("TunerFrequencyNumber", number.Number) +TunerIfFrequencyNumber = ns.class_("TunerIfFrequencyNumber", number.Number) +TunerBandwidthNumber = ns.class_("TunerBandwidthNumber", number.Number) +TunerChannelNumber = ns.class_("TunerChannelNumber", number.Number) +TunerChannelSpacingNumber = ns.class_("TunerChannelSpacingNumber", number.Number) +TunerFskDeviationNumber = ns.class_("TunerFskDeviationNumber", number.Number) +TunerMskDeviationNumber = ns.class_("TunerMskDeviationNumber", number.Number) +TunerSymbolRateNumber = ns.class_("TunerSymbolRateNumber", number.Number) +AgcCarrierSenseAbsThrNumber = ns.class_("AgcCarrierSenseAbsThrNumber", number.Number) + +TYPES = { + None: { + CONF_OUTPUT_POWER: [ + number.number_schema( + OutputPowerNumber, + unit_of_measurement=UNIT_DECIBEL_MILLIWATT, + device_class=DEVICE_CLASS_SIGNAL_STRENGTH, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.OUTPUT_POWER_MIN, + ns.OUTPUT_POWER_MAX, + 0.1, + NumberMode.NUMBER_MODE_SLIDER, + ], + }, + CONF_TUNER: { + CONF_FREQUENCY: [ + number.number_schema( + TunerFrequencyNumber, + unit_of_measurement=UNIT_KILO_HERTZ, + device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.FREQUENCY_MIN, + ns.FREQUENCY_MAX, + 1, + ], + CONF_IF_FREQUENCY: [ + number.number_schema( + TunerIfFrequencyNumber, + unit_of_measurement=UNIT_KILO_HERTZ, + device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.IF_FREQUENCY_MIN, + ns.IF_FREQUENCY_MAX, + 0.001, + ], + CONF_BANDWIDTH: [ + number.number_schema( + TunerBandwidthNumber, + unit_of_measurement=UNIT_KILO_HERTZ, + device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.BANDWIDTH_MIN, + ns.BANDWIDTH_MAX, + 0.001, + ], + CONF_CHANNEL: [ + number.number_schema( + TunerChannelNumber, + # unit_of_measurement=, + # device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.CHANNEL_MIN, + ns.CHANNEL_MAX, + 1, + # NumberMode.NUMBER_MODE_BOX, + ], + CONF_CHANNEL_SPACING: [ + number.number_schema( + TunerChannelSpacingNumber, + unit_of_measurement=UNIT_KILO_HERTZ, + device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.CHANNEL_SPACING_MIN, + ns.CHANNEL_SPACING_MAX, + 0.001, + ], + CONF_FSK_DEVIATION: [ + number.number_schema( + TunerFskDeviationNumber, + unit_of_measurement=UNIT_KILO_HERTZ, + device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.FSK_DEVIATION_MIN, + ns.FSK_DEVIATION_MAX, + 0.001, + ], + CONF_MSK_DEVIATION: [ + number.number_schema( + TunerMskDeviationNumber, + # unit_of_measurement=, + # device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.MSK_DEVIATION_MIN, + ns.MSK_DEVIATION_MAX, + 1, + ], + CONF_SYMBOL_RATE: [ + number.number_schema( + TunerSymbolRateNumber, + # unit_of_measurement=, + # device_class=DEVICE_CLASS_FREQUENCY, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.SYMBOL_RATE_MIN, + ns.SYMBOL_RATE_MAX, + 1, + ], + }, + CONF_AGC: { + CONF_CARRIER_SENSE_ABS_THR: [ + number.number_schema( + AgcCarrierSenseAbsThrNumber, + unit_of_measurement=UNIT_DECIBEL, + device_class=DEVICE_CLASS_SIGNAL_STRENGTH, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ns.CARRIER_SENSE_ABS_THR_MIN, + ns.CARRIER_SENSE_ABS_THR_MAX, + 1, + ], + }, +} + +CONFIG_SCHEMA = CC1101_COMPONENT_SCHEMA.extend( + {cv.Optional(k): v[0] for k, v in TYPES[None].items()}, + { + cv.Optional(CONF_TUNER): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_TUNER].items()} + ), + cv.Optional(CONF_AGC): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_AGC].items()} + ), + }, +) + + +async def to_code(config): + parent = await cg.get_variable(config[CONF_CC1101_ID]) + + async def new_number(c, args, setter): + # only override mode when it's set to auto in user config + if CONF_MODE not in c or c[CONF_MODE] == NumberMode.NUMBER_MODE_AUTO: + if len(args) > 3 and args[3] is not None: + c[CONF_MODE] = args[3] + n = await number.new_number( + c, min_value=args[0], max_value=args[1], step=args[2] + ) + await cg.register_parented(n, parent) + cg.add(getattr(parent, setter + "_number")(n)) + + await for_each_conf(config, TYPES, new_number) diff --git a/components/cc1101/number/number.h b/components/cc1101/number/number.h new file mode 100644 index 0000000..befb816 --- /dev/null +++ b/components/cc1101/number/number.h @@ -0,0 +1,51 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "../cc1101.h" +#include + +namespace esphome { +namespace cc1101 { + +// maybe there is a way to specialize type dependent member pointers + +template +class NumberFloat : public number::Number, public Parented { + protected: + void control(float value) override { + this->publish_state(value); + (this->parent_->*F)(value); + } +}; + +template +class NumberUInt8 : public number::Number, public Parented { + protected: + void control(float value) override { + this->publish_state(value); + (this->parent_->*F)((uint8_t) lround(value)); + } +}; + +template +class NumberInt8 : public number::Number, public Parented { + protected: + void control(float value) override { + this->publish_state(value); + (this->parent_->*F)((int8_t) lround(value)); + } +}; + +using OutputPowerNumber = NumberFloat<&CC1101Component::set_output_power>; +using TunerFrequencyNumber = NumberFloat<&CC1101Component::set_tuner_frequency>; +using TunerIfFrequencyNumber = NumberFloat<&CC1101Component::set_tuner_if_frequency>; +using TunerBandwidthNumber = NumberFloat<&CC1101Component::set_tuner_bandwidth>; +using TunerChannelNumber = NumberUInt8<&CC1101Component::set_tuner_channel>; +using TunerChannelSpacingNumber = NumberFloat<&CC1101Component::set_tuner_channel_spacing>; +using TunerFskDeviationNumber = NumberFloat<&CC1101Component::set_tuner_fsk_deviation>; +using TunerMskDeviationNumber = NumberUInt8<&CC1101Component::set_tuner_msk_deviation>; +using TunerSymbolRateNumber = NumberFloat<&CC1101Component::set_tuner_symbol_rate>; +using AgcCarrierSenseAbsThrNumber = NumberInt8<&CC1101Component::set_agc_carrier_sense_abs_thr>; + +} // namespace cc1101 +} // namespace esphome diff --git a/components/cc1101/select/__init__.py b/components/cc1101/select/__init__.py new file mode 100644 index 0000000..0e1ac46 --- /dev/null +++ b/components/cc1101/select/__init__.py @@ -0,0 +1,178 @@ +import esphome.codegen as cg +from esphome.components import select +import esphome.config_validation as cv +from esphome.const import CONF_WAIT_TIME, ENTITY_CATEGORY_CONFIG + +from .. import ( + CARRIER_SENSE_REL_THR, + CC1101_COMPONENT_SCHEMA, + CONF_AGC, + CONF_CARRIER_SENSE_REL_THR, + CONF_CC1101_ID, + CONF_FILTER_LENGTH_ASK_OOK, + CONF_FILTER_LENGTH_FSK_MSK, + CONF_FREEZE, + CONF_HYST_LEVEL, + CONF_MAGN_TARGET, + CONF_MAX_DVGA_GAIN, + CONF_MAX_LNA_GAIN, + CONF_MODULATION, + CONF_RX_ATTENUATION, + CONF_SYNC_MODE, + CONF_TUNER, + FILTER_LENGTH_ASK_OOK, + FILTER_LENGTH_FSK_MSK, + FREEZE, + HYST_LEVEL, + MAGN_TARGET, + MAX_DVGA_GAIN, + MAX_LNA_GAIN, + MODULATION, + RX_ATTENUATION, + SYNC_MODE, + WAIT_TIME, + for_each_conf, + ns, +) + +RxAttenuationSelect = ns.class_("RxAttenuationSelect", select.Select) +TunerSyncModeSelect = ns.class_("TunerSyncModeSelect", select.Select) +TunerModulationSelect = ns.class_("TunerModulationSelect", select.Select) +AgcMagnTargetSelect = ns.class_("AgcMagnTargetSelect", select.Select) +AgcMaxLnaGainSelect = ns.class_("AgcMaxLnaGainSelect", select.Select) +AgcMaxDvgaGainSelect = ns.class_("AgcMaxDvgaGainSelect", select.Select) +AgcCarrierSenseRelThrSelect = ns.class_("AgcCarrierSenseRelThrSelect", select.Select) +AgcFilterLengthFskMskSelect = ns.class_("AgcFilterLengthFskMskSelect", select.Select) +AgcFilterLengthAskOokSelect = ns.class_("AgcFilterLengthAskOokSelect", select.Select) +AgcFreezeSelect = ns.class_("AgcFreezeSelect", select.Select) +AgcWaitTimeSelect = ns.class_("AgcWaitTimeSelect", select.Select) +AgcHystLevelSelect = ns.class_("AgcHystLevelSelect", select.Select) + + +TYPES = { + None: { + CONF_RX_ATTENUATION: [ + select.select_schema( + RxAttenuationSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + RX_ATTENUATION, + ], + }, + CONF_TUNER: { + CONF_SYNC_MODE: [ + select.select_schema( + TunerSyncModeSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + SYNC_MODE, + ], + CONF_MODULATION: [ + select.select_schema( + TunerModulationSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + MODULATION, + ], + }, + CONF_AGC: { + CONF_MAGN_TARGET: [ + select.select_schema( + AgcMagnTargetSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + MAGN_TARGET, + ], + CONF_MAX_LNA_GAIN: [ + select.select_schema( + AgcMaxLnaGainSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + MAX_LNA_GAIN, + ], + CONF_MAX_DVGA_GAIN: [ + select.select_schema( + AgcMaxDvgaGainSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + MAX_DVGA_GAIN, + ], + CONF_CARRIER_SENSE_REL_THR: [ + select.select_schema( + AgcCarrierSenseRelThrSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + CARRIER_SENSE_REL_THR, + ], + CONF_FILTER_LENGTH_FSK_MSK: [ + select.select_schema( + AgcFilterLengthFskMskSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + FILTER_LENGTH_FSK_MSK, + ], + CONF_FILTER_LENGTH_ASK_OOK: [ + select.select_schema( + AgcFilterLengthAskOokSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + FILTER_LENGTH_ASK_OOK, + ], + CONF_FREEZE: [ + select.select_schema( + AgcFreezeSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + FREEZE, + ], + CONF_WAIT_TIME: [ + select.select_schema( + AgcWaitTimeSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + WAIT_TIME, + ], + CONF_HYST_LEVEL: [ + select.select_schema( + AgcHystLevelSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + HYST_LEVEL, + ], + }, +} + +CONFIG_SCHEMA = CC1101_COMPONENT_SCHEMA.extend( + {cv.Optional(k): v[0] for k, v in TYPES[None].items()}, + { + cv.Optional(CONF_TUNER): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_TUNER].items()} + ), + cv.Optional(CONF_AGC): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_AGC].items()} + ), + }, +) + + +async def to_code(config): + parent = await cg.get_variable(config[CONF_CC1101_ID]) + + async def new_select(c, args, setter): + s = await select.new_select(c, options=list(args[0].keys())) + await cg.register_parented(s, parent) + cg.add(getattr(parent, setter + "_select")(s)) + + await for_each_conf(config, TYPES, new_select) diff --git a/components/cc1101/select/select.h b/components/cc1101/select/select.h new file mode 100644 index 0000000..b091d09 --- /dev/null +++ b/components/cc1101/select/select.h @@ -0,0 +1,34 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "../cc1101.h" + +namespace esphome { +namespace cc1101 { + +template +class Select : public select::Select, public Parented { + protected: + void control(const std::string &value) override { + this->publish_state(value); + if (auto index = this->active_index()) { + (this->parent_->*F)((T) *index); + } + } +}; + +using RxAttenuationSelect = Select; +using TunerSyncModeSelect = Select; +using TunerModulationSelect = Select; +using AgcMagnTargetSelect = Select; +using AgcMaxLnaGainSelect = Select; +using AgcMaxDvgaGainSelect = Select; +using AgcCarrierSenseRelThrSelect = Select; +using AgcFilterLengthFskMskSelect = Select; +using AgcFilterLengthAskOokSelect = Select; +using AgcFreezeSelect = Select; +using AgcWaitTimeSelect = Select; +using AgcHystLevelSelect = Select; + +} // namespace cc1101 +} // namespace esphome diff --git a/components/cc1101/switch/__init__.py b/components/cc1101/switch/__init__.py new file mode 100644 index 0000000..ad7ff57 --- /dev/null +++ b/components/cc1101/switch/__init__.py @@ -0,0 +1,78 @@ +import esphome.codegen as cg +from esphome.components import switch +import esphome.config_validation as cv +from esphome.const import DEVICE_CLASS_SWITCH, ENTITY_CATEGORY_CONFIG + +from .. import ( + CC1101_COMPONENT_SCHEMA, + CONF_AGC, + CONF_CARRIER_SENSE_ABOVE_THRESHOLD, + CONF_CC1101_ID, + CONF_DC_BLOCKING_FILTER, + CONF_LNA_PRIORITY, + CONF_TUNER, + for_each_conf, + ns, +) + +DcBlockingFilterSwitch = ns.class_("DcBlockingFilterSwitch", switch.Switch) +CarrierSenseAboveThresholdSwitch = ns.class_( + "CarrierSenseAboveThresholdSwitch", switch.Switch +) +AgcLnaPrioritySwitch = ns.class_("AgcLnaPrioritySwitch", switch.Switch) + +TYPES = { + None: { + CONF_DC_BLOCKING_FILTER: [ + switch.switch_schema( + DcBlockingFilterSwitch, + device_class=DEVICE_CLASS_SWITCH, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ], + }, + CONF_TUNER: { + CONF_CARRIER_SENSE_ABOVE_THRESHOLD: [ + switch.switch_schema( + CarrierSenseAboveThresholdSwitch, + device_class=DEVICE_CLASS_SWITCH, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ], + }, + CONF_AGC: { + CONF_LNA_PRIORITY: [ + switch.switch_schema( + AgcLnaPrioritySwitch, + device_class=DEVICE_CLASS_SWITCH, + entity_category=ENTITY_CATEGORY_CONFIG, + # icon=ICON_, + ), + ], + }, +} + +CONFIG_SCHEMA = CC1101_COMPONENT_SCHEMA.extend( + {cv.Optional(k): v[0] for k, v in TYPES[None].items()}, + { + cv.Optional(CONF_TUNER): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_TUNER].items()} + ), + cv.Optional(CONF_AGC): cv.Schema( + {cv.Optional(k): v[0] for k, v in TYPES[CONF_AGC].items()} + ), + }, +) + + +async def to_code(config): + parent = await cg.get_variable(config[CONF_CC1101_ID]) + + async def new_switch(c, args, setter): + s = await switch.new_switch(c) + await cg.register_parented(s, parent) + cg.add(getattr(parent, setter + "_switch")(s)) + + await for_each_conf(config, TYPES, new_switch) diff --git a/components/cc1101/switch/switch.h b/components/cc1101/switch/switch.h new file mode 100644 index 0000000..8af9b6a --- /dev/null +++ b/components/cc1101/switch/switch.h @@ -0,0 +1,22 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "../cc1101.h" + +namespace esphome { +namespace cc1101 { + +template class Switch : public switch_::Switch, public Parented { + protected: + void write_state(bool value) override { + this->publish_state(value); + (this->parent_->*F)(value); + } +}; + +using DcBlockingFilterSwitch = Switch<&CC1101Component::set_dc_blocking_filter>; +using CarrierSenseAboveThresholdSwitch = Switch<&CC1101Component::set_tuner_carrier_sense_above_threshold>; +using AgcLnaPrioritySwitch = Switch<&CC1101Component::set_agc_lna_priority>; + +} // namespace cc1101 +} // namespace esphome diff --git a/components/cc1101/text/__init__.py b/components/cc1101/text/__init__.py new file mode 100644 index 0000000..7091620 --- /dev/null +++ b/components/cc1101/text/__init__.py @@ -0,0 +1,133 @@ +from typing import Optional + +import esphome.codegen as cg +from esphome.components import mqtt, text, web_server +import esphome.config_validation as cv +from esphome.const import ( + CONF_ENTITY_CATEGORY, + CONF_ICON, + CONF_ID, + CONF_MQTT_ID, + CONF_WEB_SERVER, + ENTITY_CATEGORY_CONFIG, +) +from esphome.core import CORE +from esphome.cpp_generator import MockObjClass +from esphome.cpp_helpers import setup_entity + +from .. import CC1101_COMPONENT_SCHEMA, CONF_CC1101_ID, for_each_conf, ns + +CONF_DUMMY_TEXT = "dummy_text" +ICON_FORMAT_TEXT = "mdi:format-text" + +DummyText = ns.class_("DummyText", text.Text) + +# The text component isn't implemented the same way as switch, select, number. +# It is not possible to create our own text platform as it is now, so I adopted +# the necessary code from those. + +_TEXT_SCHEMA = ( + cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) + .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) + .extend( + { + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextComponent), + } + ) +) + +_UNDEF = object() + + +def text_schema( + class_: MockObjClass = _UNDEF, + *, + entity_category: str = _UNDEF, + device_class: str = _UNDEF, + icon: str = _UNDEF, +): + schema = _TEXT_SCHEMA + if class_ is not _UNDEF: + schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)}) + if entity_category is not _UNDEF: + schema = schema.extend( + { + cv.Optional( + CONF_ENTITY_CATEGORY, default=entity_category + ): cv.entity_category + } + ) + if icon is not _UNDEF: + schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon}) + return schema + + +TEXT_SCHEMA = text_schema() # for compatibility + + +async def setup_text_core_( + var, + config, + *, + min_length: Optional[int], + max_length: Optional[int], + pattern: Optional[str], +): + await setup_entity(var, config) + cg.add(var.traits.set_min_length(min_length)) + cg.add(var.traits.set_max_length(max_length)) + if pattern is not None: + cg.add(var.traits.set_pattern(pattern)) + + if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: + mqtt_ = cg.new_Pvariable(mqtt_id, var) + await mqtt.register_mqtt_component(mqtt_, config) + + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) + + +async def register_text( + var, + config, + min_length: Optional[int] = 0, + max_length: Optional[int] = 255, + pattern: Optional[str] = None, +): + if not CORE.has_id(config[CONF_ID]): + var = cg.Pvariable(config[CONF_ID], var) + cg.add(cg.App.register_text(var)) + await setup_text_core_( + var, config, min_length=min_length, max_length=max_length, pattern=pattern + ) + + +TYPES = { + None: { + CONF_DUMMY_TEXT: [ + text_schema( + DummyText, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_FORMAT_TEXT, + ), + 0, + 64, + ] + }, +} + +CONFIG_SCHEMA = CC1101_COMPONENT_SCHEMA.extend( + {cv.Optional(k): v[0] for k, v in TYPES[None].items()}, +) + + +async def to_code(config): + parent = await cg.get_variable(config[CONF_CC1101_ID]) + + async def new_text(c, args, setter): + var = cg.new_Pvariable(c[CONF_ID]) + await register_text(var, c, min_length=args[0], max_length=args[1]) + await cg.register_parented(var, parent) + cg.add(getattr(parent, setter + "_text")(var)) + + await for_each_conf(config, TYPES, new_text) diff --git a/components/cc1101/text/text.h b/components/cc1101/text/text.h new file mode 100644 index 0000000..373f034 --- /dev/null +++ b/components/cc1101/text/text.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/text/text.h" +#include "../cc1101.h" + +namespace esphome { +namespace cc1101 { + +class DummyText : public text::Text, public Parented { + protected: + void control(const std::string &value) override { + this->publish_state(value); + // this->parent_->set_dummy_text(value); + } +}; + +} // namespace cc1101 +} // namespace esphome