From c4ca5b1169b7755f6e93861a70098ab4c18b208e Mon Sep 17 00:00:00 2001 From: Mateusz Furga Date: Fri, 7 Aug 2026 18:54:31 +0200 Subject: [PATCH 1/5] docs: add ASK-OOK repeater example --- examples/Repeater/Repeater.ino | 147 +++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 examples/Repeater/Repeater.ino diff --git a/examples/Repeater/Repeater.ino b/examples/Repeater/Repeater.ino new file mode 100644 index 0000000..76eb64a --- /dev/null +++ b/examples/Repeater/Repeater.ino @@ -0,0 +1,147 @@ +// Universal OOK signal repeater using asynchronous serial mode. +// +// This listens for a 433 MHz OOK burst, records it, and re-transmits an identical +// copy. It works WITHOUT knowing the data rate, bit encoding or framing, because +// it never decodes anything. We simply time the high/low pulse widths on capture +// and reproduce those exact timings on replay. + +#include +#include + +using namespace CC1101; + +#define GDO0_PIN 15 + +Radio radio(/* cs */ 10, /* gd0 */ GDO0_PIN); + +const double FREQUENCY = 433.8; // MHz (must match the signal you repeat) +// A high data rate makes the TX modulator oversample finely so arbitrary +// captured timings are reproduced well. +const double DATA_RATE = 250.0; // kBaud +const int8_t OUTPUT_POWER = 10; // dBm +const size_t MAX_PULSES = 512; // capture buffer size (transitions) +const size_t MIN_PULSES = 16; // ignore bursts shorter than this (noise) +const uint32_t PULSE_MIN_US = 450; // a real burst must start with a HIGH pulse this long +const uint32_t GAP_US = 6000; // idle this long; end of burst + +uint16_t pulses[MAX_PULSES]; +size_t pulseCount = 0; +uint8_t firstLevel = HIGH; + +static inline uint16_t clampDur(uint32_t us) { + return (us > 0xffff) ? 0xffff : (uint16_t)us; +} + +// delayMicroseconds() is only reliable up to ~16 ms on some cores; chunk longer +// waits so replay timing stays accurate on every platform. +static inline void delayUs(uint32_t us) { + while (us > 16000) { + delayMicroseconds(16000); + us -= 16000; + } + delayMicroseconds(us); +} + +bool captureBurst() { + pulseCount = 0; + + // Wait for the start of a real burst: a HIGH pulse at least PULSE_MIN_US long. + unsigned long edge; + for (;;) { + while (digitalRead(GDO0_PIN) == HIGH) {} + while (digitalRead(GDO0_PIN) == LOW) { yield(); } + edge = micros(); + while (digitalRead(GDO0_PIN) == HIGH) {} + + uint32_t high = micros() - edge; + if (high >= PULSE_MIN_US) { + firstLevel = HIGH; + pulses[0] = clampDur(high); + pulseCount = 1; + edge = micros(); + break; + } + } + + // Record alternating segments until the line stays idle for GAP_US or the buffer fills. + uint8_t level = LOW; + while (pulseCount < MAX_PULSES) { + while (digitalRead(GDO0_PIN) == level) { + if (micros() - edge > GAP_US) { + return pulseCount >= MIN_PULSES; + } + } + unsigned long now = micros(); + pulses[pulseCount++] = clampDur(now - edge); + edge = now; + level = !level; + } + + return true; +} + +void replayBurst() { + if (radio.serialTransmit() != STATUS_OK) { + Serial.println(F("serialTransmit() failed")); + return; + } + + uint8_t level = firstLevel; + for (size_t i = 0; i < pulseCount; i++) { + digitalWrite(GDO0_PIN, level); + delayUs(pulses[i]); + level = !level; + } + + // Leave the carrier off and hold it there for 12 bit periods before leaving TX + // (CC1101 errata SWRZ020E, "Extra Byte Transmitted in TX"). + digitalWrite(GDO0_PIN, LOW); + delayUs((uint32_t)(12 * 1000.0 / DATA_RATE + 0.5)); + + radio.idle(); +} + +void setup() { + Serial.begin(115200); + delay(3000); + Serial.println(F("Starting ...")); + delay(1000); + + if (radio.begin() == STATUS_CHIP_NOT_FOUND) { + Serial.println(F("Chip not found!")); + while (true) { delay(1000); } + } + + radio.setModulation(MOD_ASK_OOK); + radio.setFrequency(FREQUENCY); + radio.setRxBandwidth(64.0); + radio.setDataRate(DATA_RATE); + radio.setOutputPower(OUTPUT_POWER); + + radio.setPacketFormat(PKT_FORMAT_ASYNC_SERIAL); + + Serial.println(F("Repeater ready.")); +} + +void loop() { + radio.serialReceive(); + Serial.println(F("Listening for a burst ...")); + + if (!captureBurst()) { + radio.idle(); + return; + } + radio.idle(); + + Serial.print(F("Captured ")); + Serial.print(pulseCount); + Serial.println(F(" pulses, ready to repeat")); + + delay(3000); + + replayBurst(); + + Serial.println(F("Burst replayed")); + + delay(500); +} From 6013cbe4bc26d1a41a01dc8dddf341a1dfd0175f Mon Sep 17 00:00:00 2001 From: Mateusz Furga Date: Fri, 7 Aug 2026 19:01:31 +0200 Subject: [PATCH 2/5] add RX_BANDWIDTH const --- examples/Repeater/Repeater.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/Repeater/Repeater.ino b/examples/Repeater/Repeater.ino index 76eb64a..654f8b8 100644 --- a/examples/Repeater/Repeater.ino +++ b/examples/Repeater/Repeater.ino @@ -15,6 +15,7 @@ using namespace CC1101; Radio radio(/* cs */ 10, /* gd0 */ GDO0_PIN); const double FREQUENCY = 433.8; // MHz (must match the signal you repeat) +const double RX_BANDWIDTH = 64.0; // kHz (wide enough for the signal you repeat) // A high data rate makes the TX modulator oversample finely so arbitrary // captured timings are reproduced well. const double DATA_RATE = 250.0; // kBaud @@ -114,7 +115,7 @@ void setup() { radio.setModulation(MOD_ASK_OOK); radio.setFrequency(FREQUENCY); - radio.setRxBandwidth(64.0); + radio.setRxBandwidth(RX_BANDWIDTH); radio.setDataRate(DATA_RATE); radio.setOutputPower(OUTPUT_POWER); From 3e356c506434335596b28450239e6c6b8be8ff3a Mon Sep 17 00:00:00 2001 From: Mateusz Furga Date: Fri, 7 Aug 2026 19:06:01 +0200 Subject: [PATCH 3/5] address CR Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- examples/Repeater/Repeater.ino | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/Repeater/Repeater.ino b/examples/Repeater/Repeater.ino index 654f8b8..63af743 100644 --- a/examples/Repeater/Repeater.ino +++ b/examples/Repeater/Repeater.ino @@ -49,10 +49,10 @@ bool captureBurst() { // Wait for the start of a real burst: a HIGH pulse at least PULSE_MIN_US long. unsigned long edge; for (;;) { - while (digitalRead(GDO0_PIN) == HIGH) {} + while (digitalRead(GDO0_PIN) == HIGH) { yield(); } while (digitalRead(GDO0_PIN) == LOW) { yield(); } edge = micros(); - while (digitalRead(GDO0_PIN) == HIGH) {} + while (digitalRead(GDO0_PIN) == HIGH) { yield(); } uint32_t high = micros() - edge; if (high >= PULSE_MIN_US) { @@ -119,13 +119,25 @@ void setup() { radio.setDataRate(DATA_RATE); radio.setOutputPower(OUTPUT_POWER); + // Receive/transmit raw serial data: disable packet engine features. + radio.setPacketLengthMode(PKT_LEN_MODE_INFINITE); + radio.setSyncMode(SYNC_MODE_NO_PREAMBLE); + radio.setCrc(false); + radio.setDataWhitening(false); + radio.setManchester(false); + radio.setFEC(false); + radio.setPacketFormat(PKT_FORMAT_ASYNC_SERIAL); Serial.println(F("Repeater ready.")); } void loop() { - radio.serialReceive(); + if (radio.serialReceive() != STATUS_OK) { + Serial.println(F("serialReceive() failed")); + delay(1000); + return; + } Serial.println(F("Listening for a burst ...")); if (!captureBurst()) { From 078e4b868d1cfbdd7ac035ed39a0786bc11ba2eb Mon Sep 17 00:00:00 2001 From: Mateusz Furga Date: Fri, 7 Aug 2026 19:16:10 +0200 Subject: [PATCH 4/5] add CS_PIN --- examples/Repeater/Repeater.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/Repeater/Repeater.ino b/examples/Repeater/Repeater.ino index 63af743..497305b 100644 --- a/examples/Repeater/Repeater.ino +++ b/examples/Repeater/Repeater.ino @@ -10,9 +10,10 @@ using namespace CC1101; -#define GDO0_PIN 15 +#define CS_PIN 10 +#define GDO0_PIN 15 // data -Radio radio(/* cs */ 10, /* gd0 */ GDO0_PIN); +Radio radio(/* cs */ CS_PIN, /* gd0 */ GDO0_PIN); const double FREQUENCY = 433.8; // MHz (must match the signal you repeat) const double RX_BANDWIDTH = 64.0; // kHz (wide enough for the signal you repeat) From c8e76e80a3e515eba023c1bac51c9c8594b9d00c Mon Sep 17 00:00:00 2001 From: Mateusz Furga Date: Fri, 7 Aug 2026 19:17:50 +0200 Subject: [PATCH 5/5] update --- examples/Repeater/Repeater.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Repeater/Repeater.ino b/examples/Repeater/Repeater.ino index 497305b..2d07c50 100644 --- a/examples/Repeater/Repeater.ino +++ b/examples/Repeater/Repeater.ino @@ -13,8 +13,6 @@ using namespace CC1101; #define CS_PIN 10 #define GDO0_PIN 15 // data -Radio radio(/* cs */ CS_PIN, /* gd0 */ GDO0_PIN); - const double FREQUENCY = 433.8; // MHz (must match the signal you repeat) const double RX_BANDWIDTH = 64.0; // kHz (wide enough for the signal you repeat) // A high data rate makes the TX modulator oversample finely so arbitrary @@ -26,6 +24,8 @@ const size_t MIN_PULSES = 16; // ignore bursts shorter than this (noise) const uint32_t PULSE_MIN_US = 450; // a real burst must start with a HIGH pulse this long const uint32_t GAP_US = 6000; // idle this long; end of burst +Radio radio(/* cs */ CS_PIN, /* gd0 */ GDO0_PIN); + uint16_t pulses[MAX_PULSES]; size_t pulseCount = 0; uint8_t firstLevel = HIGH;