ESP32-SDR-TRX: Turn a $5 ESP32-S3 into a 2.4 GHz SDR Transceiver for SSB, FM, and RTTY

TL;DR: esp32-sdr-trx by Jochen Hammes (built upon foundational research by h0m3us3r) is an open-source (0BSD) project that transforms an ordinary, off-the-shelf ESP32-S3 microcontroller development board into a functional 2.4 GHz (13 cm band) Software-Defined Transceiver. On receive (espdr-rx), it captures 1.84 to 2.79 GHz RF signals by decimating the internal 16 Msps Wi-Fi ADC stream with 128-bit Xtensa SIMD vector DSP down to 250/333 ksps, serving it over an integrated rtl_tcp server to SDR++ and GNU Radio. On transmit (espdr-tx), it implements polar modulation to generate clean SSB (USB/LSB), Narrowband FM, and RTTY across the 2320 to 2450 MHz amateur radio band, complete with an on-chip predictive thermal drift compensation model that reduces carrier frequency drift from 2,000 Hz down to under 70 Hz.


What Is ESP32-SDR-TRX?

ESP32-SDR-TRX is an open-source software-defined radio transceiver firmware and host driver suite that uses the undocumented internal radio diagnostic pathways and test-tone oscillators of the ESP32-S3 chip to receive 1.84-2.79 GHz signals and transmit SSB, FM, and RTTY on the 13 cm amateur band without external mixers, FPGAs, or transverters.

+-----------------------------------------------------------------------------------------+
|                              ESP32-SDR-TRX SYSTEM TOPOLOGY                              |
+-----------------------------------------------------------------------------------------+

 [ 2.4 GHz RF Front-End ]          [ ESP32-S3 Dual-Core SoC ]          [ Host PC & Applications ]
 ┌──────────────────────┐          ┌───────────────────────────┐       ┌────────────────────────┐
 │ 2.4 GHz PCB Antenna  │          │ Xtensa LX7 @ 240 MHz      │       │ Linux Host System      │
 │ (Wi-Fi RF Switch &   │ ──RF────►│ - Undocumented ADC Dump   │       │ (Python 3.10+ Package) │
 │  Direct LNA / Mixer) │          │ - 16 Msps 10-bit I/Q Ingest│       └───────────┬────────────┘
 └──────────────────────┘          └─────────────┬─────────────┘                   │
                                                 │                                 │
 [ Receive Pipeline (espdr-rx) ]                 │                                 │
 ┌───────────────────────────────────────────────▼─────────────┐                   │
 │ Core 1 SIMD DSP Engine:                                     │                   │
 │ - 128-bit PIE Vector Instructions (ee.vmulas.s16.accx)      │                   │
 │ - SIMD CIC Decimation + 73-Tap Symmetric FIR Filter         │                   │
 │ - 16 Msps Stream Decimated to 250 ksps / 333 ksps (int8 I/Q)│                   │
 └───────────────────────────────┬─────────────────────────────┘                   │
                                 │                                                 │
                                 │ USB Serial/JTAG (0.87 MB/s Stream)              │
                                 ▼                                                 ▼
 ┌─────────────────────────────────────────────────────────────┐       ┌────────────────────────┐
 │ Host rtl_tcp Server Bridge (127.0.0.1:1234)                 │ ────► │ SDR++ / GNU Radio /    │
 │ - Exposes standard RTL-SDR TCP protocol to SDR clients      │       │ GQRX Panadapters       │
 └─────────────────────────────────────────────────────────────┘       └────────────────────────┘

 [ Transmit Pipeline (espdr-tx) ]
 ┌─────────────────────────────────────────────────────────────┐       ┌────────────────────────┐
 │ Host Polar Modulation & Predictive Thermal Model:           │ ◄──── │ Audio Input:           │
 │ - Reads on-chip sensor (TX_OP_TEMP) to model crystal curve  │       │ - WAV File / Soundcard │
 │ - 40,000 updates/sec register steering (Polar Mod)          │       │ - Live Mic Pipe (PCM)  │
 │ - Compensates +2000 Hz thermal drift down to <70 Hz         │       │ - RTTY Baudot Text     │
 └───────────────────────────────┬─────────────────────────────┘       └────────────────────────┘
                                 │
                                 │ High-Speed Control Commands (40 kHz)
                                 ▼
 ┌─────────────────────────────────────────────────────────────┐       ┌────────────────────────┐
 │ ESP32-S3 Internal RF Transmitter Engine:                    │ ──RF─►│ 13 cm Amateur Band     │
 │ - Steers Carrier Frequency & Amplitude (Polar Modulation)   │       │ (2320 - 2450 MHz)      │
 │ - Modes: USB, LSB, Narrow FM (±2.5 kHz), RTTY (170 Hz FSK)  │       │ Signal Output          │
 └─────────────────────────────────────────────────────────────┘       └────────────────────────┘

For years, amateur radio operators experimenting with Software-Defined Radio on microwave frequencies (such as the 2.4 GHz 13 cm band and QO-100 satellite uplinks) needed dedicated SDR hardware like the HackRF One, ADALM-Pluto, LimeSDR, or specialized microwave transverters costing hundreds of dollars.

ESP32-SDR-TRX upends this paradigm. By exploiting hidden diagnostic registers inside the low-cost Espressif ESP32-S3 microcontroller, developer Jochen Hammes demonstrated that a generic development board can operate as a functional, bidirectional microwave transceiver.


ESP32-SDR-TRX vs Dedicated SDR Transceivers

To understand where ESP32-SDR-TRX fits into the amateur radio ecosystem, let us compare its hardware and operational characteristics against established SDR platforms:

Transceiver Platform Retail Price (Approx.) Frequency Coverage RX Bandwidth & Decimation TX Modulation Method Host Interface FPGA / Co-Proc
ESP32-SDR-TRX (ESP32-S3) $5 – $9 1.84-2.79 GHz (RX & 13cm TX) 250 / 333 kS/s (SIMD on CPU) Software Polar Mod USB 2.0 CDC/JTAG None (Xtensa CPU)
HackRF One $150 – $350 1 MHz-6 GHz (Half-Duplex) Up to 20 MS/s (MAX2837/ADC) Direct I/Q Streaming USB 2.0 HighSpeed CPLD + ARM Cortex
ADALM-Pluto $200 – $280 70 MHz-6 GHz (Full-Duplex) Up to 61 MS/s (AD9363/FPGA) Full-Duplex I/Q Stream USB 2.0 / ETH IP Xilinx Zynq FPGA
LimeSDR Mini v2 $350 – $400 10 MHz-3.5GHz (Full-Duplex) Up to 30 MS/s (LMS7002M) Full-Duplex I/Q Stream USB 3.0 SuperSpeed Intel MAX 10 FPGA
RTL-SDR Blog V4 $30 – $40 500 kHz-1.7G (No 2.4 GHz) Up to 2.4 MS/s (R828D) RX ONLY (No Transmit) USB 2.0 FullSpeed Realtek ASIC

How Reception Works: 128-Bit SIMD DSP Decimation (espdr-rx)

The ESP32-S3 Wi-Fi subsystem contains an internal diagnostic dump engine capable of routing raw ADC samples from the radio front-end directly into system SRAM at 16 Msps (10-bit I/Q pairs).

+─────────────────────────────────────────────────────────────────────────────────────────+
|                           ON-CHIP SIMD DSP DECIMATION PIPELINE                          |
+─────────────────────────────────────────────────────────────────────────────────────────+

  16 Msps 10-bit I/Q Samples (Undocumented Wi-Fi ADC Dump)
              │
              ▼
  ┌───────────────────────────────────────────────────────────────────────────────────────┐
  │ SRAM Bank Dual-Buffer Ingest (Unpacked via SIMD ee.vunzip.16 Instructions)            │
  └───────────────────────────────────┬───────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌───────────────────────────────────────────────────────────────────────────────────────┐
  │ Stage 1: Vectorized CIC Decimation Filter (64-tap SIMD FIR on unpacked samples)       │
  │ - Reduces sample rate by factor R1 = 16 or 12                                         │
  └───────────────────────────────────┬───────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌───────────────────────────────────────────────────────────────────────────────────────┐
  │ Stage 2: 73-Tap Symmetric Half-Band FIR Filter (ee.vmulas.s16.accx)                   │
  │ - 8 Signed 16-bit MAC operations per instruction cycle                                │
  │ - Final Output: 250 ksps (±100 kHz) or 333 ksps (±133 kHz) at int8 I/Q resolution    │
  └───────────────────────────────────┬───────────────────────────────────────────────────┘
                                      │
                                      ▼
  USB Serial/JTAG FIFO (0.87 MB/s Output) ──► Host rtl_tcp Server (127.0.0.1:1234)

The Microcontroller Cycle Budget

Streaming raw 16 Msps I/Q would require 32 MB/s of bandwidth, far exceeding the ESP32-S3’s 0.87 MB/s USB Serial/JTAG throughput. Decimation must happen inside the microcontroller in real time.

A standard C implementation required 764,000 clock cycles per processing block (159% of the CPU budget), causing immediate buffer overflow. Jochen Hammes optimized the firmware to fit the Xtensa LX7 dual-core architecture:

  1. Core 1 Isolation: Core 1 runs the DSP decimation loop entirely from dedicated instruction RAM (.core1_text), eliminating ROM bus contention and cache misses.
  2. Xtensa PIE SIMD Vectorization: Using the ee.vmulas.s16.accx vector multiply-accumulate instruction, the 73-tap FIR filter executes in just ten vector passes per channel instead of 146 scalar instructions.
  3. Execution Time: Optimized SIMD execution cut processing time down to 288,000 cycles (60% of CPU budget), sustaining stable 250 ksps and 333 ksps streaming without lost samples.
  4. rtl_tcp Compatibility: The host driver creates a virtual rtl_tcp server on port 1234, allowing SDR applications like SDR++, GQRX, and GNU Radio to connect to the ESP32-S3 as if it were a standard RTL-SDR dongle.

How Transmission Works: Polar Modulation on 13 cm (espdr-tx)

Unlike conventional SDR transmitters that push continuous complex I/Q sample buffers to an expensive DAC, ESP32-S3 uses Software Polar Modulation.

+─────────────────────────────────────────────────────────────────────────────────────────+
|                           POLAR MODULATION TRANSMIT ENGINE                              |
+─────────────────────────────────────────────────────────────────────────────────────────+

  Audio Source (Microphone / WAV / RTTY Text)
              │
              ▼
  ┌───────────────────────────────────────────────────────────────────────────────────────┐
  │ Audio Processing & Conditioning: Bandpass Filter (300-3000 Hz) + Speech AGC + Clipper │
  └───────────────────────────────────┬───────────────────────────────────────────────────┘
                                      │
                                      ▼
  ┌───────────────────────────────────────────────────────────────────────────────────────┐
  │ Polar Coordinate Transformation (40,000 updates/second):                              │
  │ - Amplitude Envelope Component r(t)                                                   │
  │ - Phase / Frequency Deviation Component theta'(t)                                     │
  └───────────────────┬───────────────────────────────────────────┬───────────────────────┘
                      │                                           │
                      ▼                                           ▼
  ┌───────────────────────────────────────┐   ┌───────────────────────────────────────────┐
  │ Carrier Amplitude Register Steering   │   │ Carrier Frequency Register Steering       │
  │ (Output Power Level Adjustment)       │   │ (PLL Modulator + Thermal Drift Correction)│
  └───────────────────┬───────────────────┘   └───────────────────┬───────────────────────┘
                      │                                           │
                      └─────────────────────┬─────────────────────┘
                                            ▼
                              ESP32-S3 RF Output Stage
                              (2.32 to 2.45 GHz Amateur Band)

By decomposing voice or digital signals into amplitude envelope r(t) and instantaneous phase/frequency theta'(t) at 40,000 updates per second, the transmitter drives the ESP32-S3’s internal frequency synthesizer and output level registers directly.

Supported Modulation Modes

  • Single Sideband (SSB): Generates clean Upper Sideband (USB) and Lower Sideband (LSB) voice signals. Measured performance demonstrates 29 to 63 dB suppression of unwanted sidebands and third-order intermodulation distortion (IMD3) between 33 and 54 dB down. A selectable pilot carrier (5% default) simplifies tuning on remote receivers.
  • Narrowband FM (NBFM): Generates voice FM with standard ±2.5 kHz deviation and +6 dB/octave voice pre-emphasis.
  • Radioteletype (RTTY): Transmits standard 45.45 baud, 170 Hz shift FSK text using US Baudot encoding.

Thermal Drift Compensation: The Predictive Crystal Model

A critical challenge when transmitting at 2.4 GHz with consumer microcontrollers is thermal drift. The ESP32-S3 development board relies on a standard 40 MHz quartz crystal without temperature compensation (non-TCXO).

+─────────────────────────────────────────────────────────────────────────────────────────+
|                           THERMAL DRIFT BEHAVIOR & COMPENSATION                         |
+─────────────────────────────────────────────────────────────────────────────────────────+

  Frequency Excursion (Hz)
   +4000 Hz ──┐                                 [ Uncorrected Drift: +4,400 Hz runaway ]
              │                                  (Completely breaks RTTY & SSB reception)
   +3000 Hz ──┼───────────────────────           
              │                      /
   +2000 Hz ──┼───────────----------/
              │          /
   +1000 Hz ──┼────────-/
              │       /
       0 Hz ──┼──────/─────────────────────────────────────────────────────────────────
              │  [ Corrected with --thermal Model: Residual Drift Stays Within ±66 Hz ]
   -1000 Hz ──┴────────────────────────────────────────────────────────────────────────
              0s     15s     30s     45s     60s     75s     90s     105s    120s  (Time)

During transmission, the ESP32-S3 heats up rapidly, climbing from an idle 42°C to over 57°C. Multiplied up to 2.35 GHz, this thermal rise causes the carrier to drift by +2,000 to +4,400 Hz within two minutes (drifting at rates up to 200 Hz per second). Because RTTY decoders only tolerate ~85 Hz of error and SSB voice degrades significantly with off-frequency tuning, uncorrected transmissions quickly become unintelligible.

How the Thermal Model Operates

Jochen Hammes implemented a predictive thermal correction algorithm in espdr.thermal:

  1. Sensor Ingest: The host software reads the ESP32-S3 internal temperature sensor (TX_OP_TEMP) before each transmission.
  2. Thermal Curve Modeling: Models the quartz crystal’s parabolic frequency response around its turning point t0, combined with a multi-exponential chip heating curve (τ ≈ 137 s) and an early switch-on transient.
  3. Real-Time Offset Injection: Dynamically applies the inverse calculated frequency offset to every 25-microsecond control frame sent to the transmitter.
  4. Measured Results: On tested boards, frequency drift during a 2-minute transmission dropped from +2,050 Hz down to +27 to +66 Hz, allowing stable RTTY decodes and crisp SSB speech.

Practical Setup and Quick Start Guide

Setting up an ESP32-S3 SDR transceiver requires a dual-USB ESP32-S3 development board (such as the generic ESP32-S3-WROOM-1 N16R8 dual Type-C board) and a Linux host.

+─────────────────────────────────────────────────────────────────────────────────────────+
|                               QUICK START INSTALLATION                                  |
+─────────────────────────────────────────────────────────────────────────────────────────+

  # 1. Download installer and wheel from GitHub Releases
  curl -fsSL -O https://github.com/jochenhammes/esp32-sdr-trx/releases/latest/download/install-linux.sh
  curl -fsSL -O https://github.com/jochenhammes/esp32-sdr-trx/releases/latest/download/esp32_sdr_trx-0.2.0-py3-none-any.whl

  # 2. Make executable and install with soundcard audio support
  chmod +x install-linux.sh
  ./install-linux.sh esp32_sdr_trx-0.2.0-py3-none-any.whl --audio

1. Running the Receiver (espdr-rx)

Connect the ESP32-S3 USB-UART bridge port and run:

espdr-rx

The tool automatically flashes or loads the optimized receiver firmware into RAM and starts the rtl_tcp server on 127.0.0.1:1234. Open SDR++, select RTL-TCP as the source, enter 127.0.0.1:1234, and click play to explore 1.84-2.79 GHz spectrum.

2. Transmitting SSB, FM, and RTTY (espdr-tx)

Transmitting on 2.32-2.45 GHz requires a valid Amateur Radio license.

# Transmit USB voice from a pre-recorded WAV file on 2350 MHz
espdr-tx --accept-licence -f 2350 -m usb -i speech.wav --ppm 5.4

# Transmit USB voice live from your computer microphone
espdr-tx -f 2350 -m usb -i soundcard --power -6

# Pipe live audio from ALSA arecord
arecord -f S16_LE -r 16000 -c 1 | espdr-tx -f 2350 -m usb -i - --rate 16000

# Transmit an RTTY text message with standard 170 Hz shift
espdr-tx -f 2350 -m rtty --text "RYRY CQ CQ DE 9M2PJU 9M2PJU K" --ppm 5.4

Frequently Asked Questions (FAQ)

What hardware do I need to run ESP32-SDR-TRX?

You need an ESP32-S3 development board with dual USB ports (one native USB CDC/JTAG, one USB-UART bridge), two USB-C cables, and a Linux computer running Python 3.10 or newer.

Do I need an amateur radio license to use ESP32-SDR-TRX?

Receiving (1.84 to 2.79 GHz) is open to anyone. Transmitting on the 13 cm amateur band (2320 to 2450 MHz) requires a valid amateur radio operator license.

What is the output power of the ESP32-S3 transmitter?

The transmitter emits low uncalibrated RF power (typically several microwatts) directly from the development board’s antenna port, making it suitable for lab bench testing, local experimenters, or driving an external transverter amplifier.

Can I use SDR++ or GNU Radio with this receiver?

Yes. Running espdr-rx launches a standard rtl_tcp server on port 1234. SDR++, GNU Radio, GQRX, and any client compatible with RTL-SDR TCP streams can connect directly.

How does the thermal drift correction work?

The host software reads the ESP32-S3’s internal temperature sensor before transmission, models the crystal’s thermal curve, and dynamically adjusts the PLL frequency 40,000 times a second to keep the carrier within ±66 Hz.


Sources and Further Reading


73 de 9M2PJU

Post Comment