To build a reliable Raspberry Pi RF transmitter, skip the ubiquitous 5V FS1000A modules found in cheap kits. Instead, use a SYN115 433MHz ASK transmitter paired with a Raspberry Pi 4 Model B or Raspberry Pi 5. The SYN115 operates natively at 3.3V logic, matching the Pi’s GPIO tolerance, and uses a PLL-based crystal oscillator that prevents the frequency drift common in cheaper SAW-based transmitters. Because Linux is not a real-time OS, software-timed bit-banging will introduce jitter that breaks RF decoding. We solve this by using the pigpio library to leverage the Pi's hardware DMA for microsecond-accurate pulse generation.
Hardware BOM and Pin Mapping
Before wiring anything up, verify you have the correct module variant. The FS1000A requires 5V for usable range and will slowly degrade a Pi's 3.3V regulator if back-fed. The SYN115 is the correct choice for direct Pi GPIO connection.
Estimated Time: 45 minutes
Target Board: Raspberry Pi 4 Model B (Rev 1.4) or Pi 5 running Raspberry Pi OS Bookworm (64-bit).
Bill of Materials (2026 Pricing)
| Component | Exact Variant | Est. Price | Notes |
|---|---|---|---|
| Microcontroller | Raspberry Pi 4 Model B (4GB) | $55.00 | Pi 5 also works; ensure active cooling. |
| RF Transmitter | SYN115 433MHz ASK TX | $2.50 | Must have the copper coil and crystal. |
| Antenna Wire | 22 AWG Solid Core | $0.10 | Cut exactly to 17.3 cm (quarter-wave). |
| Wiring | Female-to-Female Dupont | $3.00 | Keep leads under 10cm to reduce parasitic capacitance. |
Pin Mapping Table
Wire the SYN115 to the Pi using BCM GPIO numbering. Do not use the physical pin numbers for the software configuration.
| SYN115 Pin | Pi BCM GPIO | Pi Physical Pin | Electrical Spec |
|---|---|---|---|
| VCC | N/A (3.3V Rail) | Pin 1 | 3.3V to 5V (3.3V yields ~15mA draw) |
| GND | N/A (Ground) | Pin 6 | Common ground required |
| DATA (TX) | GPIO 17 | Pin 11 | 3.3V Logic High / 0V Logic Low |
| ANT | N/A | N/A | Solder 17.3cm wire directly to pad |
Software Setup and Compilable Python Code
The standard RPi.GPIO library is deprecated and suffers from severe timing jitter on multi-core Pis running modern Linux kernels. We use pigpio, which offloads pulse generation to the Pi's hardware DMA controller, guaranteeing microsecond precision required by OOK (On-Off Keying) receivers like the PT2262.
First, install the daemon and Python bindings. On Raspberry Pi OS Bookworm, use a virtual environment:
sudo apt update
sudo apt install pigpio python3-pigpio
sudo systemctl enable pigpiod
sudo systemctl start pigpiod
python3 -m venv ~/rf_env
source ~/rf_env/bin/activate
pip install pigpio
Below is the complete, production-ready Python script. It constructs a 24-bit RF waveform in hardware memory and transmits it without CPU jitter.
import pigpio
import sys
import time
# --- PIN DEFINITIONS & RF CONFIG ---
TX_PIN = 17 # BCM GPIO 17 (Physical Pin 11)
RF_CODE = 0xABCDEF # 24-bit hex code to transmit
PULSE_LENGTH = 350 # Microseconds (syncs with standard PT2262 receivers)
BAUD_RATE = 2000 # Bits per second
BIT_DELAY = 1000000 // BAUD_RATE # Microseconds per bit
TRANSMIT_REPEATS = 5 # Number of times to repeat the packet
def build_waveform(pi, code, bits=24):
"""Constructs a pigpio waveform for OOK transmission."""
wf = []
# Sync preamble (optional but recommended for AGC settling)
wf.append(pigpio.pulse(1 << TX_PIN, 0, PULSE_LENGTH))
wf.append(pigpio.pulse(0, 1 << TX_PIN, PULSE_LENGTH * 31))
# Data bits
for i in range(bits - 1, -1, -1):
if (code >> i) & 1:
# Logic 1: Long pulse, short gap
wf.append(pigpio.pulse(1 << TX_PIN, 0, PULSE_LENGTH * 3))
wf.append(pigpio.pulse(0, 1 << TX_PIN, PULSE_LENGTH))
else:
# Logic 0: Short pulse, long gap
wf.append(pigpio.pulse(1 << TX_PIN, 0, PULSE_LENGTH))
wf.append(pigpio.pulse(0, 1 << TX_PIN, PULSE_LENGTH * 3))
pi.wave_clear()
pi.wave_add_generic(wf)
return pi.wave_create()
def main():
# Initialize pigpio connection to the daemon
pi = pigpio.pi()
if not pi.connected:
print("Error: Could not connect to pigpio daemon. Is 'pigpiod' running?")
sys.exit(1)
pi.set_mode(TX_PIN, pigpio.OUTPUT)
pi.write(TX_PIN, 0) # Ensure TX is low before starting
print(f"Building waveform for code: {hex(RF_CODE)}...")
wave_id = build_waveform(pi, RF_CODE)
if wave_id >= 0:
print(f"Transmitting {TRANSMIT_REPEATS} times...")
pi.wave_send_repeat(wave_id)
# Wait for transmission to complete
while pi.wave_tx_busy():
time.sleep(0.1)
pi.wave_tx_stop()
pi.wave_delete(wave_id)
print("Transmission complete.")
else:
print("Error: Failed to create waveform.")
pi.stop()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nTransmission aborted by user.")
sys.exit(0)
Debugging: Exact Errors and the "First Three" Checks
RF debugging on Linux is notoriously frustrating because failures happen silently at the physical layer. Before rewriting your code, run through the First Three Checks:
- Is the antenna exactly 17.3 cm? The SYN115 output impedance is matched to a quarter-wave monopole at 433.92 MHz. A coiled or missing wire drops effective radiated power (ERP) by over 90%, making it look like a software failure when the receiver simply isn't seeing the signal.
- Is the
pigpioddaemon actually running? The Python library is just a client. If the background service crashed or isn't enabled on boot, the hardware DMA never initializes. - Is the receiver's baud rate matched? If your receiver (e.g., an Arduino with the
RCSwitchlibrary) expects a 2000 bps signal and your Pi's pulse length drifts, the automatic gain control (AGC) on the receiver will reject the packet as noise.
Ranked Causes for Exact Error Strings
If the script crashes, match your terminal output to these exact strings:
| Exact Error String | Ranked Causes | Fix |
|---|---|---|
pigpio.error: 'gpio not initialized' |
1. pigpiod service is stopped.2. Port 8888 blocked by firewall. |
Run sudo systemctl start pigpiod. Check status with systemctl status pigpiod. |
RuntimeError: No access to /dev/mem |
1. Running legacy RPi.GPIO as non-root.2. User not in gpio group. |
Switch to pigpio (which runs as root via daemon) or add user: sudo usermod -aG gpio $USER. |
ModuleNotFoundError: No module named 'pigpio' |
1. Installed via apt but running in a Python venv.2. Wrong Python binary invoked. |
Activate your venv and run pip install pigpio. Ensure you use python3, not python. |
pigpio.error: 'no more room for waveforms' |
1. Previous script crashed without calling pi.wave_clear().2. DMA memory exhausted. |
Restart the daemon: sudo systemctl restart pigpiod to flush the DMA buffer. |
time.sleep() between bit-banged pulses. Always build the entire packet into a pigpio waveform buffer and let the DMA handle the timing, as shown in the code above.
Scaling Up: Extending or Simplifying the RF Link
Once you have basic 433MHz OOK transmission working, you will eventually hit the limits of ASK modulation: it is unidirectional, highly susceptible to noise, and limited to low baud rates. Here is how to adapt your architecture based on your end goal.
How to Simplify the Build
If you are struggling with Linux timing, daemon management, or Python virtual environments, offload the RF transmission to an ESP32. Use the Raspberry Pi as the high-level logic controller (running Home Assistant, Node-RED, or a web server) and send MQTT messages to an ESP32. The ESP32 runs a bare-metal RTOS that handles RF bit-banging flawlessly via the RCSwitch or ESP8266_RC_Switch libraries. This removes the Pi from the physical RF layer entirely.
How to Extend the Build (Protocol Comparison)
If you need bidirectional communication, encryption, or longer range, you must abandon ASK OOK modules. Below is a decision matrix for upgrading your Pi GPIO RF stack.
| Module | Protocol | Interface | Range | Best Use Case |
|---|---|---|---|---|
| SYN115 | ASK / OOK | Single GPIO | ~20m | Triggering cheap smart plugs, garage doors, weather stations. |
| CC1101 | FSK / GFSK | SPI | ~100m | Custom sensor networks, bidirectional data, packet acknowledgment. |
| SX1276 (LoRa) | LoRa (CSS) | SPI | 1km+ | Agriculture, perimeter security, low-power remote telemetry. |
| nRF24L01+ | GFSK (2.4GHz) | SPI | ~30m | High-speed local data (audio/mouse), but terrible wall penetration. |
Migration Note: Moving from the SYN115 to an SPI-based module like the CC1101 requires shifting from pigpio bit-banging to the Pi's hardware SPI bus (/dev/spidev0.0). You will need to enable SPI via sudo raspi-config and use Python's spidev library alongside a hardware abstraction library like SmartRF or RadioLib (via C++ bindings). Ensure you use logic level shifters if you source 5V SPI modules; the Pi's SPI pins are strictly 3.3V tolerant.






