Arduino PulseWire Transceiver Library
Loading...
Searching...
No Matches
RxDriverESP32.h
1#pragma once
2#include <driver/rmt_rx.h>
3#include <driver/rmt_tx.h>
4#include <freertos/FreeRTOS.h>
5#include <freertos/queue.h>
6#include <freertos/task.h>
7#include <stddef.h>
8#include <stdint.h>
9
10#include "TransceiverConfig.h"
11#include "pulse/RxDriver.h"
12#include "pulse/TxDriver.h"
13#include "pulse/codecs/Codec.h"
14#include "pulse/tools/Logger.h"
15#include "pulse/tools/RingBuffer.h"
16#include "pulse/tools/Vector.h"
17
18namespace pulsewire {
19
20bool rmt_rx_done_callback(rmt_channel_handle_t channel,
21 const rmt_rx_done_event_data_t* edata,
22 void* user_data) {
23 BaseType_t high_task_wakeup = pdFALSE;
24 QueueHandle_t receive_queue = (QueueHandle_t)user_data;
25 // send the received RMT symbols to the parser task
26 xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
27 // return whether any task is woken up
28 return high_task_wakeup == pdTRUE;
29}
30
52class RxDriverESP32 : public RxDriver {
53 public:
63 : _codec(codec), _rxPin(pin), _useChecksum(useChecksum) {}
64
65 void setFrameSize(uint16_t size) override { _frameSize = size; }
66
67 void setRxBufferSize(size_t size) override { _rxBufferSize = size; }
68
69 bool begin(uint32_t bitFrequencyHz) override {
70 Logger::info("begin: RxDriverESP32 with bitFrequencyHz=%d, pin=%d",
71 bitFrequencyHz, _rxPin);
72 if (isActive && _freqHz == bitFrequencyHz) {
73 return true;
74 }
75 if (isActive) {
76 Logger::info("Reinitializing RxDriverESP32 with new frequency: %d Hz",
78 end();
79 }
80 _freqHz = bitFrequencyHz;
81 if (!_codec.begin(bitFrequencyHz)) {
82 return false;
83 }
84 size_t symbols = _frameSize * 8;
85 int n64 = (symbols / 64) + 1;
86 symbols = n64 * 64;
87
88 if (n64 > 7){
89 Logger::error("Frame size too large for RMT buffer: %d symbols (max 448)",
90 symbols);
91 return false;
92 }
93
94 uint8_t dma = (_frameSize > 64) ? 1 : 0;
95 rmt_rx_channel_config_t rx_config = {.gpio_num = (gpio_num_t)_rxPin,
97 .resolution_hz = 1000000,
99 .flags = {
100 .invert_in = 0,
101 .with_dma = dma,
102 }};
103
104 // Setup channel, try with DMA first if requested
107 if ((rmt_result != ESP_OK || rx_channel == nullptr) && dma) {
109 "RMT RX channel with DMA failed (%d), retrying without DMA",
110 rmt_result);
111 rx_config.flags.with_dma = 0;
113 }
114 if (rmt_result != ESP_OK || rx_channel == nullptr) {
115 Logger::error("RMT RX channel initialization failed: %d", rmt_result);
116 return false;
117 }
118 _rxChannel = rx_channel;
119
120 esp_err_t enable_result = rmt_enable(_rxChannel);
121 if (enable_result != ESP_OK) {
122 Logger::error("Failed to enable RX channel: %d", enable_result);
123 return false;
124 }
125
126 receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));
128 .on_recv_done = rmt_rx_done_callback,
129 };
131 rmt_rx_register_event_callbacks(_rxChannel, &cbs, receive_queue);
132 if (cb_result != ESP_OK) {
133 Logger::error("Failed to register RX event callbacks: %d", cb_result);
134 return false;
135 }
136
137 _rxBuffer.resize(_rxBufferSize);
138 _symbols.resize(symbols); // Each symbol has 2 edges, +1 for safety margin
139 _edgeBuffer.resize(
140 _frameSize *
141 _codec.getEdgeCount()); // Ensure edge buffer can hold all edges
142 Logger::info("_frameSize=%d", _frameSize);
143 Logger::info("_rxBufferSize=%d", _rxBufferSize);
144 Logger::info("_symbols size=%d", _symbols.size());
145 Logger::info("_edgeBuffer size=%d", _edgeBuffer.size());
146 isActive = true;
147 return true;
148 }
149
150 void end() override {
151 if (_rxChannel) {
152 rmt_disable(_rxChannel);
153 rmt_del_channel(_rxChannel);
154 _rxChannel = nullptr;
155 }
156 isActive = false;
157 }
158
159 size_t readBytes(uint8_t* buffer, size_t length) override {
160 if (_rxBuffer.available() == 0) {
161 // No data available, attempt to read a new frame
162 readFrame();
163 }
164 return _rxBuffer.readArray(buffer, length);
165 }
166
167 int available() override {
168 if (_rxBuffer.available() == 0) {
169 // No data available, attempt to read a new frame
170 readFrame();
171 }
172 return _rxBuffer.available();
173 }
174
175 protected:
176 size_t _rxBufferSize = 1024;
177 size_t _frameSize = 0;
178 uint8_t _rxPin;
179 uint32_t _freqHz;
180 rmt_channel_handle_t _rxChannel = nullptr;
181 RingBuffer<uint8_t> _rxBuffer;
182 RingBuffer<OutputEdge> _edgeBuffer;
184 Codec& _codec;
185 bool _useChecksum = false;
186 bool isActive = false;
187 QueueHandle_t receive_queue;
188
189 size_t readFrame() {
190 size_t totalRead = 0;
191 if (_rxChannel == nullptr) {
192 Logger::error("readFrame() called but RX channel is not initialized");
193 return 0;
194 }
195
196 rmt_receive_config_t rx_cfg = {
197 .signal_range_min_ns = 10,
198 .signal_range_max_ns =
199 static_cast<uint32_t>(_codec.getEndOfFrameDelayUs() * 1000),
200 };
201
202 rmt_symbol_word_t* symbols_data = _symbols.data();
203 size_t symbols_size = (_symbols.size()) * sizeof(rmt_symbol_word_t);
204
205 Logger::debug("cm_receive: waiting for frame (max %d symbols)",
206 _symbols.size());
207 esp_err_t err =
208 rmt_receive(_rxChannel, symbols_data, symbols_size, &rx_cfg);
209 if (err != ESP_OK) {
210 Logger::error("RMT receive failed: %d", err);
211 return 0;
212 }
213
214 // wait for the RX-done signal
215 rmt_rx_done_event_data_t rx_data{};
216 esp_err_t esp_err = xQueueReceive(receive_queue, &rx_data,
217 2 * _codec.getEndOfFrameDelayUs() / 1000);
218 if (esp_err != pdTRUE) {
219 Logger::error("Failed to receive RMT data from queue: %d", esp_err);
220 return 0;
221 }
222
223 // Process symbols into edges and feed into edge buffer
224 toEdges(rx_data.received_symbols, rx_data.num_symbols, _edgeBuffer);
225
226 Logger::debug("Received %d symbols, converted to %d edges",
227 rx_data.num_symbols, _edgeBuffer.available());
228 // process edges into codec and fill RX buffer
229 _codec.reset();
230 while (!_edgeBuffer.isEmpty()) {
231 OutputEdge edge;
232 _edgeBuffer.read(edge);
233 Logger::debug("RX edge: level=%d duration=%d us", edge.level,
234 edge.pulseUs);
235 uint8_t data = 0;
236 if (_codec.decodeEdge(edge.pulseUs, edge.level, data)) {
237 if (_rxBuffer.write(data) == 0) {
238 Logger::error("RX buffer overflow: size=%d, available=%d",
239 _rxBuffer.size(), _rxBuffer.available());
240 }
241 }
242 }
243 return _rxBuffer.available();
244 }
245
249 Logger::debug("toEdges: converting %d symbols to edges", num_symbols);
250 bool is_end = false;
251 int processed = 0;
252 for (size_t i = 0; i < num_symbols; ++i) {
253 bool level = symbols[i].level0;
254 uint32_t duration = symbols[i].duration0;
255 edges.write(OutputEdge(level, duration));
256
257 level = symbols[i].level1;
258 duration = symbols[i].duration1;
259 edges.write(OutputEdge(level, duration));
260 }
261 }
262};
263
264} // namespace pulsewire
Abstract base class for IR protocol encoding and decoding.
Definition Codec.h:53
virtual int getEndOfFrameDelayUs()=0
virtual bool decodeEdge(uint32_t durationUs, bool level, uint8_t &result)=0
Edge-based decoding for protocol-agnostic RX drivers.
virtual size_t getEdgeCount() const =0
Get the number of protocol symbols (bits, pulses, etc.) per encoded byte.
virtual bool begin(uint32_t bitFrequencyHz)
initialization method for codecs that require setup before use (e.g., loading PIO programs,...
Definition Codec.h:70
virtual void reset()
Reset the internal state of the codec.
Definition Codec.h:84
static void debug(const char *format,...)
Log a debug message with formatting.
Definition Logger.h:82
static void error(const char *format,...)
Log an error message with formatting.
Definition Logger.h:37
static void warning(const char *format,...)
Log a warning message with formatting.
Definition Logger.h:52
static void info(const char *format,...)
Log an informational message with formatting.
Definition Logger.h:67
Manchester encoding/decoding utility class for IR communication.
int readArray(T *dest, size_t len)
Read up to len elements into dest, returns number of elements read.
Definition RingBuffer.h:94
High-performance ESP32 IR RX driver using the RMT peripheral and Manchester decoding.
void end() override
Stop the receiver.
void toEdges(rmt_symbol_word_t *symbols, size_t num_symbols, RingBuffer< OutputEdge > &edges)
Convert RMT symbols to edges and feed into codec.
void setFrameSize(uint16_t size) override
Set the expected frame size for dynamic data reception.
int available() override
Get the number of bytes available in the internal buffer.
bool begin(uint32_t bitFrequencyHz) override
Start the receiver.
size_t readBytes(uint8_t *buffer, size_t length) override
RxDriverESP32(ManchesterCodec &codec, uint8_t pin, bool useChecksum=false)
void setRxBufferSize(size_t size) override
Set the size of the internal RX buffer.
Abstract base class for IR receivers.
Definition RxDriver.h:10
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
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