Arduino PulseWire Transceiver Library
Loading...
Searching...
No Matches
TxDriverESP32.h
1
2#pragma once
3#include <driver/rmt_rx.h>
4#include <driver/rmt_tx.h>
5#include <freertos/FreeRTOS.h>
6#include <freertos/queue.h>
7#include <freertos/task.h>
8#include <stddef.h>
9#include <stdint.h>
10
11#include "../TransceiverConfig.h"
12#include "pulse/Codec.h"
13#include "pulse/RingBuffer.h"
14#include "pulse/RxDriver.h"
15#include "pulse/TxDriver.h"
16#include "pulse/TxDriverCommon.h"
17#include "pulse/TxProtocol.h"
18#include "pulse/Vector.h"
19
20namespace pulsewire {
21
27 public:
28 TxProtocolESP32() = default;
29
30 void setFrameSize(uint16_t size) {
31 _frameSize = size;
32 _itemsBuffer.resize(
33 size * 2); // Each byte can produce up to 2 edges (for Manchester)
34 }
35
36 bool begin(Codec* p_codec, uint8_t pin) {
37 this->_codec = p_codec;
38 this->_txPin = pin;
39 if (!_codec->begin()) {
40 Logger::error("Codec initialization failed");
41 return false;
42 }
43 // Enable DMA for large frames
44 uint8_t dma = (_frameSize > 64) ? 1 : 0;
45 rmt_tx_channel_config_t tx_config = {.gpio_num = (gpio_num_t)_txPin,
47 .resolution_hz = 1000000,
49 .flags = {
50 .invert_out = 0,
51 .with_dma = dma,
52 }};
55 if (tx_result != ESP_OK || tx_channel == nullptr) {
56 Logger::error("RMT TX channel initialization failed");
57 return false;
58 }
59 // Encoder setup
63 if (enc_result != ESP_OK || encoder == nullptr) {
64 Logger::error("RMT encoder initialization failed");
65 return false;
66 }
67 _txEncoder = encoder;
68 // Carrier config
69 rmt_transmit_config_t transmit_config = {.loop_count = 0, .flags = {}};
70 _txChannel = tx_channel;
71 _txTransmitConfig = transmit_config;
72 return true;
73 }
74
75 void sendPreamble() override {
76 if (is_frame_closed) {
77 sum = 0;
78 output.clear();
79 // Insert preamble edges at the start
80 _codec->getPreamble().getEdges(output);
81 is_frame_closed = false;
82 sum = 0;
83 }
84 }
85
86 void sendData(const uint8_t* data, uint8_t len) override {
87 if (len == 0) return;
88 // process bytes
89 for (int byteIdx = 0; byteIdx < len; ++byteIdx) {
90 uint8_t b = data[byteIdx];
91 sum += b;
92 _codec->encode(b, bitPeriod, output);
93 }
94
95 // Convert OutputSpec to RMT symbols (step by 2)
96 size_t symbolCount = output.size() / 2;
97 if (_itemsBuffer.size() < symbolCount) _itemsBuffer.resize(symbolCount);
98 for (size_t i = 0, j = 0; i + 1 < output.size(); i += 2, ++j) {
99 _itemsBuffer[j].duration0 = output[i].pulseUs;
100 _itemsBuffer[j].level0 = output[i].level ? 1 : 0;
101 _itemsBuffer[j].duration1 = output[i + 1].pulseUs;
102 _itemsBuffer[j].level1 = output[i + 1].level ? 1 : 0;
103 }
104 if (_txChannel && _txEncoder) {
105 esp_err_t err = rmt_transmit(_txChannel, _txEncoder, _itemsBuffer.data(),
107 &_txTransmitConfig);
108 if (err != ESP_OK) {
109 Logger::error("RMT transmission failed: %d", err);
110 }
111 }
112 output.clear();
113 _itemsBuffer.clear();
114 }
115
116 void sendEnd(bool& useChecksum, bool isDelayAfterFrame) {
117 // Optionally append checksum
118 if (is_frame_closed) return;
119 if (useChecksum) {
120 _codec->encode(sum, bitPeriod, output);
121 sum = 0;
122 }
123
124 // add delay
125 if (isDelayAfterFrame) {
126 output.push_back(OutputEdge(_codec->getEndOfFrameUs(), 0));
127 output.push_back(OutputEdge(0, 0));
128 }
129 // Convert OutputSpec to RMT symbols
130 size_t symbolCount = output.size() / 2;
131 if (_itemsBuffer.size() < symbolCount) _itemsBuffer.resize(symbolCount);
132 for (size_t i = 0, j = 0; i + 1 < output.size(); i += 2, ++j) {
133 _itemsBuffer[j].duration0 = output[i].pulseUs;
134 _itemsBuffer[j].level0 = output[i].level ? 1 : 0;
135 _itemsBuffer[j].duration1 = output[i + 1].pulseUs;
136 _itemsBuffer[j].level1 = output[i + 1].level ? 1 : 0;
137 }
138 if (_txChannel && _txEncoder) {
139 esp_err_t err = rmt_transmit(_txChannel, _txEncoder, _itemsBuffer.data(),
141 &_txTransmitConfig);
142 if (err != ESP_OK) {
143 Logger::error("RMT transmission failed: %d", err);
144 }
145 }
146 is_frame_closed = true;
147 }
148
149 bool isFrameClosed() const override { return is_frame_closed; }
150
151 protected:
152 Codec* _codec = nullptr;
153 uint8_t _txPin;
154 uint16_t _frameSize = 64;
155 rmt_channel_handle_t _txChannel = nullptr;
156 Vector<rmt_symbol_word_t> _itemsBuffer;
157 rmt_encoder_handle_t _txEncoder = nullptr;
158 rmt_transmit_config_t _txTransmitConfig = {};
159 Vector<OutputEdge> output;
160 uint8_t sum = 0;
161 bool is_frame_closed = true;
162};
163
169 public:
178 TxDriverESP32(Codec& codec, uint8_t pin, uint32_t carrierHz = CARRIER_HZ,
179 uint32_t freqHz = DEFAULT_BIT_FREQ_HZ, uint8_t duty = 33,
180 bool useChecksum = false) {
181 begin(codec, pin, freqHz, useChecksum);
182 }
183
184 bool begin(Codec& codec, uint8_t pin, uint32_t carrierHz = CARRIER_HZ,
185 uint32_t freqHz = DEFAULT_BIT_FREQ_HZ, uint8_t duty = 33,
186 bool useChecksum = false) {
187 if (!TxDriverCommon::begin(protocol, codec, pin, freqHz, useChecksum)) {
188 Logger::error("TxDriverCommon initialization failed");
189 return false;
190 }
191 if (!protocol.begin(&codec, pin)) {
192 Logger::error("TxProtocolESP32 initialization failed");
193 return false;
194 }
195 }
196
197 protected:
198 TxProtocolESP32 protocol;
199
200 void sendPreamble() { protocol.sendPreamble(); }
201
202 void sendData(const uint8_t* data, uint8_t len) {
203 protocol.sendData(data, len, _bitPeriod);
204 }
205
206 void sendEnd() { protocol.sendEnd(_useChecksum, true); }
207};
208
209} // namespace pulsewire
Abstract base class for IR protocol encoding and decoding.
Definition Codec.h:41
virtual size_t encode(uint8_t byte, Vector< OutputEdge > &output)
Fill output vector with protocol-specific OutputSpec(s) for a byte.
Definition Codec.h:162
virtual bool begin(uint32_t bitFrequencyHz)
initialization method for codecs that require setup before use (e.g., loading PIO programs,...
Definition Codec.h:56
Preamble & getPreamble()
Get the preamble detector associated with this codec.
Definition Codec.h:77
static void error(const char *format,...)
Log an error message with formatting.
Definition Logger.h:37
virtual int getEdges(pulsewire::Vector< pulsewire::OutputEdge > &output) const =0
Returns the expected preamble edges for this protocol.
Provides common logic for transmitting signals using various framing modes.
ESP32-specific TxDriver implementation that uses TxProtocolESP32 for transmission.
TxDriverESP32(Codec &codec, uint8_t pin, uint32_t carrierHz=CARRIER_HZ, uint32_t freqHz=DEFAULT_BIT_FREQ_HZ, uint8_t duty=33, bool useChecksum=false)
ESP32-specific TxProtocol implementation using RMT for precise timing and DMA support.
Abstract base class for defining transmission protocols.
Definition TxProtocol.h:125
Small, header-only vector replacement for non-STL environments.
Definition Vector.h:29
void resize(size_t n)
Resize vector to n elements.
Definition Vector.h:150
void clear()
Remove all elements.
Definition Vector.h:120
void push_back(const T &value)
Add element to end of vector.
Definition Vector.h:92
T * data()
Pointer to underlying data.
Definition Vector.h:106
size_t size() const
Number of elements in vector.
Definition Vector.h:116
Specifies a single IR signal segment for protocol-agnostic transmission.
Definition Preamble.h:23