Arduino PulseWire Transceiver Library
Loading...
Searching...
No Matches
RxDriverArduino.h
1#pragma once
2#include <Arduino.h>
3
4#include "TransceiverConfig.h"
5#include "pulse/RxDriver.h"
6#include "pulse/codecs/Codec.h"
7#include "pulse/codecs/ManchesterCodec.h"
8#include "pulse/tools/Logger.h"
9#include "pulse/tools/RingBuffer.h"
10#include "pulse/tools/Vector.h"
11
12namespace pulsewire {
13
23class RxDriverInt : public RxDriver {
24 public:
25 virtual void handleInterrupt() = 0;
26};
27
28#if !defined(HAS_INTERRUPT_ARG)
29
35 public:
36 static bool attach(uint8_t pin, RxDriverInt* instance) {
37 initISR();
38 for (int i = 0; i < 10; i++) {
39 if (!_isrData[i].active || _isrData[i].pin == pin) {
40 _isrData[i].instance = instance;
41 _isrData[i].active = true;
42 _isrData[i].pin = pin;
43 attachInterrupt(digitalPinToInterrupt(pin), _isrData[i].isr, CHANGE);
44 return true;
45 }
46 }
47 return false;
48 }
49
50 static bool isAttached(uint8_t pin) {
51 for (int i = 0; i < 10; i++) {
52 if (_isrData[i].active && _isrData[i].pin == pin) {
53 return true;
54 }
55 }
56 return false;
57 }
58
59 static void detach(uint8_t pin) {
60 for (int i = 0; i < 10; i++) {
61 if (_isrData[i].active && _isrData[i].pin == pin) {
63 _isrData[i].instance = nullptr;
64 _isrData[i].active = false;
65 _isrData[i].pin = -1;
66 break;
67 }
68 }
69 }
70
71 private:
72 struct InstanceData {
73 RxDriverInt* instance;
74 void (*isr)(void);
75 int pin;
76 bool active;
77 };
78
79 static InstanceData _isrData[10];
80
81 static void isr0() { dispatch(0); }
82 static void isr1() { dispatch(1); }
83 static void isr2() { dispatch(2); }
84 static void isr3() { dispatch(3); }
85 static void isr4() { dispatch(4); }
86 static void isr5() { dispatch(5); }
87 static void isr6() { dispatch(6); }
88 static void isr7() { dispatch(7); }
89 static void isr8() { dispatch(8); }
90 static void isr9() { dispatch(9); }
91
92 static void initISR() {
93 static bool initialized = false;
94 if (!initialized) {
95 void (*isrTable[10])() = {isr0, isr1, isr2, isr3, isr4,
96 isr5, isr6, isr7, isr8, isr9};
97 for (int i = 0; i < 10; ++i) {
98 _isrData[i].instance = nullptr;
99 _isrData[i].isr = isrTable[i];
100 _isrData[i].pin = -1;
101 _isrData[i].active = false;
102 }
103 initialized = true;
104 }
105 }
106
107 static void dispatch(int index) {
108 if (index >= 0 && index < 10 && _isrData[index].active) {
109 _isrData[index].instance->handleInterrupt();
110 }
111 }
112};
113
114#endif
115
142 public:
151 uint32_t freqHz = DEFAULT_BIT_FREQ_HZ,
152 bool useChecksum = false, uint32_t timeoutUs = 0)
153 : _pin(pin),
154 _codec(codec),
155 _freqHz(freqHz),
156 _useChecksum(useChecksum),
157 _timeoutUs(timeoutUs) {
158 reset();
159 }
160
161 void init(Codec& codec, uint8_t pin, uint32_t freqHz = DEFAULT_BIT_FREQ_HZ,
162 bool useChecksum = false, uint32_t timeoutUs = 0) {
163 TRACE();
164 _pin = pin;
165 _codec = codec;
166 _freqHz = freqHz;
167 _useChecksum = useChecksum;
168 _timeoutUs = timeoutUs;
169 reset();
170 }
171
172 //  destructor
173 ~RxDriverArduino() { end(); }
174
175 void setFrameSize(uint16_t size) override {
176 TRACE();
177 _codec.setFrameSize(size);
178 _frameSize = size;
179 _rxBuffer.resize(size * 2);
180 _edgeBuffer.resize(size * _codec.getEdgeCount() *
181 2); // enough for edges of a full frame
182 }
183
184 void setRxBufferSize(size_t size) { _rxBuffer.resize(size); }
185
186 bool begin(uint32_t bitFrequencyHz = DEFAULT_BIT_FREQ_HZ) override {
187 TRACE();
188 _freqHz = bitFrequencyHz;
189
190 // Initialize codec once
191 bool rc = _codec.begin(bitFrequencyHz);
192
193 // Calculate bit timing window from freqHz
194 _bitPeriodUs = 1000000UL / _freqHz;
195 _minUs = _bitPeriodUs / 2;
196 _maxUs = _bitPeriodUs * 2;
198 "RXDriveer: Bit frequency: %d Hz, Bit period: %d us, Timing window: "
199 "[%d, %d] us",
200 _freqHz, _bitPeriodUs, _minUs, _maxUs);
201
202 if (_timeoutUs == 0) {
203 _timeoutUs = 0.95 * _codec.getEndOfFrameDelayUs();
204 }
205 Logger::info("RXDriver: Timeout set to %d us", _timeoutUs);
206
207 // Ensure byte buffer is sized for frame
208 setFrameSize(_frameSize);
209
210 if (!_is_active) {
211 // Reset state BEFORE attaching interrupt
212 reset();
213 // Read initial pin level before first interrupt
214 pinMode(_pin, INPUT);
215 _lastLevel = digitalRead(_pin);
216 _lastEdge = micros();
217
218#ifdef HAS_INTERRUPT_ARG
219 attachInterruptArg(_pin, interruptHandler, this, CHANGE);
220#else
221 if (!ISRManager::attach(_pin, this)) {
222 return false;
223 }
224#endif
225 _is_active = true;
226 }
227 _is_open = false;
228 Logger::info("RX driver started on pin %d with codec %s with frame size %d",
229 _pin, _codec.name(), _frameSize);
230 return rc;
231 }
232
233 size_t readBytes(uint8_t* buffer, size_t length) override {
234 uint32_t timeout = millis() + _readTimeoutMs;
235 while (_rxBuffer.available() < length && millis() < timeout) {
236 processEdges();
237 checkTimeout();
238 if (_rxBuffer.available() < length) {
239 delay(10); // small delay to avoid busy waiting
240 }
241 }
242 if (_rxBuffer.size() == 0) {
243 return 0;
244 }
245 size_t count = 0;
246 count = _rxBuffer.readArray((uint8_t*)buffer, length);
247 return count;
248 }
249
250 int available() override {
251 processEdges();
252 checkTimeout();
253 int result = _rxBuffer.available();
254 Logger::debug("available(): %d", result);
255 return result;
256 }
257
258 void end() {
259 TRACE();
260 if (_is_active) {
261#ifdef HAS_INTERRUPT_ARG
262 detachInterrupt(_pin);
263#else
264 ISRManager::detach(_pin);
265#endif
266 _is_active = false;
267 }
268 }
269
270 protected:
271 volatile uint32_t _lastEdge;
272 volatile bool _lastLevel;
273 uint8_t _pin;
274 uint32_t _freqHz;
275 uint16_t _frameSize = DEFAULT_FRAME_SIZE;
276 Codec& _codec;
277 RingBuffer<uint8_t> _rxBuffer;
278 RingBuffer<OutputEdge> _edgeBuffer;
279
280 uint32_t _bitPeriodUs = 0;
281 uint32_t _minUs = 0;
282 uint32_t _maxUs = 0;
283 bool _useChecksum = false;
284 uint32_t _timeoutUs = 0;
285 volatile bool _is_active = false;
286 volatile bool _is_open = false;
287
288#ifdef HAS_INTERRUPT_ARG
289 static IRAM_ATTR void interruptHandler(void* arg) {
290 static_cast<RxDriverArduino*>(arg)->handleInterrupt();
291 }
292#endif
293
294 void IRAM_ATTR handleInterrupt() override {
295 bool newLevel = digitalRead(_pin);
296 uint32_t now = micros();
297 uint32_t duration = now - _lastEdge;
298 _lastEdge = now;
299
300 // Pass the duration and the previous level (_lastLevel)
301 OutputEdge edge{_lastLevel, duration};
302 _edgeBuffer.write(edge);
303 _lastLevel = newLevel;
304 }
305
306 // Called from loop() context — safe for heap, virtual calls, etc.
307 void processEdges() {
308 Logger::debug("processEdges");
309 bool ok = true;
310 OutputEdge edge;
311 while (!_rxBuffer.isFull() && ok) {
312 noInterrupts();
313 ok = _edgeBuffer.read(edge);
314 interrupts();
315
316 if (ok) {
317 uint8_t byte_data = 0;
318 Logger::debug("RX Processing edge: level=%d, duration=%d us",
319 edge.level, edge.pulseUs);
320 if (_codec.decodeEdge(edge.pulseUs, edge.level, byte_data)) {
321 _rxBuffer.write(byte_data);
322 _is_open = true;
323 }
324 if (edge.pulseUs > _codec.getEndOfFrameDelayUs()) {
325 Logger::debug("End of frame detected: pulse duration %d us",
326 edge.pulseUs);
327 reset();
328 }
329 }
330 }
331 }
332
333 void checkTimeout() {
334 Logger::debug("checkTimeout");
335 uint32_t now = micros();
336 uint32_t duration = now - _lastEdge;
337 uint8_t byte_data = 0;
338
339 // Process pending final edge (e.g., last stop bit)
340 if (_is_open && duration > _timeoutUs) {
341 Logger::debug("Processing final edge after timeout: duration %d us",
342 duration);
343 bool has_data = _codec.decodeEdge(_bitPeriodUs, _lastLevel, byte_data);
344 if (has_data) {
345 _rxBuffer.write(byte_data);
346 }
347 reset();
348 }
349 }
350
351 void reset() {
352 _lastEdge = micros();
353 _lastLevel = _codec.getIdleLevel();
354 _is_open = false;
355 _codec.reset();
356 }
357};
358
359} // 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.
const char * name() const
Get the name of the codec type as a string (e.g., "PulseDistance", "Manchester").
Definition Codec.h:135
virtual bool getIdleLevel() const
Provides the initial ldle state (low or hith)
Definition Codec.h:127
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
void setFrameSize(uint16_t size)
Set the Frame Size.
Definition Codec.h:114
virtual void reset()
Reset the internal state of the codec.
Definition Codec.h:84
Manager for handling multiple ISR instances on platforms without attachInterruptArg.
static void debug(const char *format,...)
Log a debug message with formatting.
Definition Logger.h:82
static void info(const char *format,...)
Log an informational message with formatting.
Definition Logger.h:67
int readArray(T *dest, size_t len)
Read up to len elements into dest, returns number of elements read.
Definition RingBuffer.h:94
Interrupt-driven Arduino RX driver for pulse-based protocols.
void end()
Stop the receiver.
void setRxBufferSize(size_t size)
Set the size of the internal RX buffer.
bool begin(uint32_t bitFrequencyHz=DEFAULT_BIT_FREQ_HZ) override
Start the receiver.
int available() override
Get the number of bytes available in the internal buffer.
size_t readBytes(uint8_t *buffer, size_t length) override
RxDriverArduino(Codec &codec, uint8_t pin, uint32_t freqHz=DEFAULT_BIT_FREQ_HZ, bool useChecksum=false, uint32_t timeoutUs=0)
void setFrameSize(uint16_t size) override
Set the expected frame size for dynamic data reception.
Interrupt-capable IR RX driver interface for platforms without attachInterruptArg.
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