Summary
Add a user-registerable callback that fires with the raw pulse/gap arrays whenever the library captures a pulse train — independent of whether any built-in decoder matched.
Motivation
Today, downstream code can register setCallback(rtl_433_Callback, ...) to receive JSON-formatted decode results, but only when a built-in decoder matches the packet. For unknown / custom protocols (with -DPUBLISH_UNPARSED), the raw pulse data is written to stdout via printf() (logprintf / alogprintf macros in include/log.h).
This makes it very difficult to implement custom protocol decoders in user code without modifying the library. In a recent project I tried three different interception paths to consume those raw pulses from user code on Arduino-ESP32 v2.x:
| Interception path |
Result |
Log.begin(LOG_LEVEL, &myPrint) (ArduinoLog redirect) |
No effect — logprintf / alogprintf use raw printf(), not ArduinoLog |
extern "C" int _write(int fd, const void *data, size_t size) |
No effect — current Arduino-ESP32 v2.x doesn't route ROM printf through newlib syscalls |
ets_install_putc1(my_putc) |
No effect — different code path on this Arduino-ESP32 build |
The only working option ended up being parsing the serial output from a separate Python process on the host PC, which doesn't help for embedded-only deployments.
Proposed API
typedef void (*rtl_433_raw_pulse_cb)(
const int* pulse_us,
const int* gap_us,
unsigned int num_pulses,
unsigned long duration_us,
int rssi
);
void rtl_433_ESP::setRawPulsesCallback(rtl_433_raw_pulse_cb cb);
Fired from the same context as the existing JSON callback in signalDecoder.cpp, right around the PUBLISH_UNPARSED block — but unconditional (fires whether or not a decoder matched). Pointers are valid only during the callback; caller must copy if they need to retain.
Use cases
- Implementing custom decoders for protocols not in rtl_433's device set (cheap proprietary remotes, off-brand sensors, pyrotechnic cue remotes, etc.)
- Logging / exporting pulse data for offline analysis (URH, triq.org/pdv)
- Building protocol-discovery tools that run on-device rather than tethered to a host PC
Happy to put together a PR if the maintainer is open to the design. Let me know if you'd prefer a different shape (e.g. a virtual method to override, or a pulse_data_t* rather than separate arrays).
Summary
Add a user-registerable callback that fires with the raw pulse/gap arrays whenever the library captures a pulse train — independent of whether any built-in decoder matched.
Motivation
Today, downstream code can register
setCallback(rtl_433_Callback, ...)to receive JSON-formatted decode results, but only when a built-in decoder matches the packet. For unknown / custom protocols (with-DPUBLISH_UNPARSED), the raw pulse data is written to stdout viaprintf()(logprintf/alogprintfmacros ininclude/log.h).This makes it very difficult to implement custom protocol decoders in user code without modifying the library. In a recent project I tried three different interception paths to consume those raw pulses from user code on Arduino-ESP32 v2.x:
Log.begin(LOG_LEVEL, &myPrint)(ArduinoLog redirect)logprintf/alogprintfuse rawprintf(), not ArduinoLogextern "C" int _write(int fd, const void *data, size_t size)printfthrough newlib syscallsets_install_putc1(my_putc)The only working option ended up being parsing the serial output from a separate Python process on the host PC, which doesn't help for embedded-only deployments.
Proposed API
Fired from the same context as the existing JSON callback in
signalDecoder.cpp, right around thePUBLISH_UNPARSEDblock — but unconditional (fires whether or not a decoder matched). Pointers are valid only during the callback; caller must copy if they need to retain.Use cases
Happy to put together a PR if the maintainer is open to the design. Let me know if you'd prefer a different shape (e.g. a virtual method to override, or a
pulse_data_t*rather than separate arrays).