ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
remote_transmitter.h
Go to the documentation of this file.
1#pragma once
2
5
6#include <vector>
7
8#if defined(USE_ESP32)
9#include <soc/soc_caps.h>
10#if SOC_RMT_SUPPORTED
11#include <driver/rmt_tx.h>
12#endif // SOC_RMT_SUPPORTED
13#endif // USE_ESP32
14
15// Enables the ISR-driven transmitter on Beken. Gated on BK7238 alone: the shadow-load PWM
16// block is shared with BK7231N, but LibreTiny builds that family against an older BDK whose
17// PWM driver has no pwm_init_param()/pwm_start(). See remote_transmitter_bk72xx.cpp.
18// Keep in sync with _NON_BLOCKING_LIBRETINY_FAMILIES in __init__.py.
19#ifdef USE_LIBRETINY_VARIANT_BK7238
20#define REMOTE_TRANSMITTER_BK_PWM
21#endif
22
24
25#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
26#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
27// IDF version 5.5.1 and above is required because of a bug in
28// the RMT encoder: https://github.com/espressif/esp-idf/issues/17244
29typedef union { // NOLINT(modernize-use-using)
30 struct {
31 uint16_t duration : 15;
32 uint16_t level : 1;
33 };
34 uint16_t val;
36
41#endif
42#endif
43
45 public Component
46#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
47 ,
49#endif
50{
51 public:
53 void setup() override;
54
55 void dump_config() override;
56
57 // transmitter setup must run after receiver setup to allow the same GPIO to be used by both
58 float get_setup_priority() const override { return setup_priority::DATA - 1; }
59
60 void set_carrier_duty_percent(uint8_t carrier_duty_percent) { this->carrier_duty_percent_ = carrier_duty_percent; }
61
62 void digital_write(bool value);
63
64#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
65 void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; }
66 void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; }
67#endif
68#if (defined(USE_ESP32) && SOC_RMT_SUPPORTED) || defined(USE_LIBRETINY_VARIANT_RTL8720C) || \
69 defined(REMOTE_TRANSMITTER_BK_PWM)
70 void set_non_blocking(bool non_blocking) { this->non_blocking_ = non_blocking; }
71#endif
72#if defined(USE_LIBRETINY_VARIANT_RTL8720C) || defined(REMOTE_TRANSMITTER_BK_PWM)
73 void loop() override;
74 // called from the envelope timer ISR trampoline; not part of the public API
76 // same, for trampolines whose SDK callback carries no user argument
77 static void advance_active_isr();
78#endif
79
82
83 protected:
84 void send_internal(uint32_t send_times, uint32_t send_wait) override;
85#if defined(USE_ESP8266) || \
86 (defined(USE_LIBRETINY) && !defined(USE_LIBRETINY_VARIANT_RTL8720C) && !defined(REMOTE_TRANSMITTER_BK_PWM)) || \
87 defined(USE_RP2) || (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
88 void await_target_time_();
90#endif
91#if defined(USE_ESP8266) || \
92 (defined(USE_LIBRETINY) && !defined(USE_RTL87XX) && !defined(REMOTE_TRANSMITTER_BK_PWM)) || defined(USE_RP2) || \
93 (defined(USE_ESP32) && !SOC_RMT_SUPPORTED)
94 void calculate_on_off_time_(uint32_t carrier_frequency, uint32_t *on_time_period, uint32_t *off_time_period);
95
96 void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec);
97
98 void space_(uint32_t usec);
99#endif
100#ifdef USE_RTL87XX
101 // Carrier frequency the PWM is currently configured for; 0 = not yet configured
103 void *pwm_{nullptr}; // pwmout_t*, opaque here to keep the SDK header out of this shared header
104#endif
105#if defined(USE_LIBRETINY_VARIANT_RTL8720C) || defined(REMOTE_TRANSMITTER_BK_PWM)
106 // Envelope chain, shared by every family that paces transmission from a hardware timer
107 // (remote_transmitter_libretiny_isr.cpp)
108 void start_isr_item_(size_t index);
109 void arm_envelope_timer_(uint32_t duration_us);
111 void deliver_completion_();
112 void wait_until_idle_();
113 void arm_chain_(uint32_t send_times, uint32_t send_wait);
114 // Hooks implemented per family: everything the chain needs from the hardware
115 bool envelope_ready_() const; // PWM claimed successfully in setup()
116 void prepare_carrier_(uint32_t carrier_frequency); // retune period, stage mark/space levels
117 void write_envelope_level_(bool mark); // drive carrier (mark) or idle (space)
118 void arm_one_shot_(uint32_t duration_us); // fire advance_envelope_isr after duration_us
120 std::vector<int32_t> isr_data_; // owned copy of the frame; temp_ may be re-encoded mid-flight
121 volatile size_t isr_index_{0};
124 volatile uint32_t isr_wait_remaining_{0}; // remainder of a duration chained across one-shots
125 volatile bool isr_in_gap_{false};
126 volatile bool transmitting_{false};
127 bool non_blocking_{false};
128 bool complete_pending_{false};
129 bool stall_aborted_{false}; // this transmission ended via abort; blocks warning clear
130#endif
131#ifdef USE_LIBRETINY_VARIANT_RTL8720C
132 float isr_mark_duty_{0.0f};
133 float isr_space_duty_{0.0f};
134#endif
135#ifdef REMOTE_TRANSMITTER_BK_PWM
136 void write_pwm_t1_(uint32_t t1_counts);
139 uint32_t isr_period_t4_{684}; // 26MHz counts; ~38kHz default until a send sets the real carrier
140 int8_t pwm_channel_{-1};
141#endif
142
143#if defined(USE_ESP32) && SOC_RMT_SUPPORTED
144 void configure_rmt_();
145 void wait_for_rmt_();
146
147#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 1)
149 std::vector<rmt_symbol_half_t> rmt_temp_;
150#else
151 std::vector<rmt_symbol_word_t> rmt_temp_;
152#endif
154 bool initialized_{false};
155 bool with_dma_{false};
156 bool eot_level_{false};
157 rmt_channel_handle_t channel_{NULL};
158 rmt_encoder_handle_t encoder_{NULL};
159 esp_err_t error_code_{ESP_OK};
160 std::string error_string_;
161 bool inverted_{false};
162 bool non_blocking_{false};
163#endif
165
168};
169
170} // namespace esphome::remote_transmitter
void send_internal(uint32_t send_times, uint32_t send_wait) override
void calculate_on_off_time_(uint32_t carrier_frequency, uint32_t *on_time_period, uint32_t *off_time_period)
void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec)
void set_carrier_duty_percent(uint8_t carrier_duty_percent)
constexpr float DATA
For components that import data from directly connected sensors like DHT.
Definition component.h:45
static void uint32_t