A local web app that controls an ATS Mini (ESP32-S3 + Si4732 shortwave/AM/FM receiver) over USB, with a searchable 50,000+ station database, live signal scanning, and an automatic Morse code (CW) decoder.
Author: James Sawyer / JSLabs — jamessawyer.co.uk | labs.jamessawyer.co.uk
- Station browser: 50,000+ shortwave, AM, and FM stations from EiBi (global shortwave schedules), the FCC (US/Canada AM+FM), and Ofcom (UK AM+FM). Search, filter by band, and see which shortwave broadcasts are on air right now based on your local time.
- Click to tune: click any station in the browser and the physical radio retunes to it over USB, in under a second for most band changes.
- RSSI signal scanning: measures real signal strength on a batch of stations and flags which ones are actually receivable right now, not just licensed to exist. Results persist across restarts.
- CW (Morse code) monitor: cycles known CW-heavy frequencies (propagation beacons, ham calling frequencies), locks onto whichever one has real keying, and decodes it live in the browser — timestamped, frequency-tagged, scrolling — until the signal stops or fades below detection.
- Audio passthrough: routes the radio's audio (via a USB sound interface connected to its headphone output) straight to your computer's speakers.
- An ATS Mini running firmware v2.34 or later, with Settings → USB Port → Ad hoc enabled (remote control is off by default).
- USB-C cable (the same one used for charging/flashing carries the serial control link).
- Optional, for CW decoding and audio passthrough: a USB audio interface with a line/mic input, cabled from the ATS Mini's headphone jack into the interface's input jack (not its output jack — cheap adapters often have both on the same unit).
pip install -r requirements.txt
./start.shstart.sh clears any stale server process, warns if the radio isn't plugged in, starts the backend, and opens http://127.0.0.1:8731 in your browser. The ATS Mini's serial port is auto-detected by USB vendor ID, so it works regardless of which /dev/cu.usbmodemNNNN path macOS assigns it.
data/stations.db ships pre-built. To refresh it from source:
python3 stations_db.py --eibi data/eibi_sked.csv \
--regulator ofcom_fm:FM:data/ofcom_fm.csv \
--regulator ofcom_am:AM:data/ofcom_am.csv \
--fcc-fm data/fcc_fm_raw.txt \
--fcc-am data/fcc_am_raw.txtSource files aren't included in this repo (the FCC dumps alone are ~23MB); download links are in stations_db.py's docstrings and the EiBi/FCC/Ofcom sites directly.
The firmware's serial protocol has no direct frequency-jump command — only relative "next band" / "next mode" stepping — so retuning across very different bands can take a few seconds and passes through intermediate bands visibly on the radio's own screen. That's a hardware/firmware limit, not something this app can fully hide.
Decoding works by capturing audio, finding the CW tone's frequency (unknown in advance — it depends on the receiver's BFO offset), isolating it with a bandpass filter, extracting the on/off keying envelope, and converting the timing into dots, dashes, and text. No PyPI package does audio-to-Morse decoding well, so this part (cw_decoder.py) is a small local implementation on top of numpy/scipy, validated against synthetic Morse test signals in test_cw_decoder.py.
The Stations tab shows the full database, 500 rows per page, ordered by frequency. Type in the search box to filter by name, country, or language; use the band dropdown to narrow to shortwave, AM, or FM. Click any row and the physical radio retunes to that frequency — same-band clicks (e.g. two shortwave stations) are near-instant, since the firmware only needs a real band change when crossing between shortwave/AM and FM.
The On air now checkbox filters shortwave listings to schedules that are currently active, converted from the database's UTC times into your browser's local time zone automatically. AM and FM entries are broadcast-license data with no schedule, so they're always treated as on air.
The meter bar at the top is always live: current frequency, band/mode, RSSI, SNR (FM only — the receiver chip doesn't compute a usable SNR value in AM/SSB modes), volume with +/- buttons, and an audio passthrough toggle.
Click Scan RSSI (visible) to measure real signal strength on whatever's currently filtered/visible (capped at 80 stations per scan, since each measurement needs a moment to settle). Once scanned, a station shows a signal-strength bar and its RSSI/SNR values, and the Audible only checkbox filters down to ones that measured as actually receivable — not just present in the database. Measurements persist across restarts, so repeated scans of different filters build up coverage over time instead of resetting.
The CW Monitor tab runs a background scan → lock → decode → release loop: it cycles a small set of known CW-heavy frequencies (propagation beacons with high duty cycles, ham calling frequencies), and the moment it detects real on/off keying it locks onto that frequency and decodes continuously, streaming the text live. When the signal goes quiet or fades below detection, that decode is pushed to the scrolling log with a timestamp, frequency, and WPM estimate, and scanning resumes.
One-shot: listen here does a single ~4 second capture-and-decode on whatever frequency the radio is currently tuned to, without touching the tuning — useful to check a specific frequency you've navigated to manually from the Stations tab.
A manual tune from the Stations tab always interrupts a running scan or CW hunt — background automation never fights you for control of the radio.
The Audio to Mac toggle in the meter bar copies the USB audio interface's input straight to your computer's default output device in real time, so whatever the radio is tuned to is audible. It needs exclusive access to the input device, so it can't run at the same time as the CW monitor (starting one will ask you to stop the other first).
- This firmware's band list has two entries both named
15M(a shortwave broadcast meter band and the 15m ham band). Band selection matches by name, so a target using that name can land on the wrong one.20M/17M/10M/30Mdon't collide with any broadcast band. - Audio passthrough plays through your computer's default output device, not inside the browser tab.
- CW decoding needs a genuinely strong, clean signal. A stock whip antenna indoors will often not be enough for weak DX or beacon traffic even when everything else is working correctly.
- RSSI scanning and CW listening are one-user-at-a-time operations: a manual tune always interrupts a running scan or the CW monitor.
- Not affiliated with, endorsed by, or supported by the ATS Mini / esp32-si4732 project, PU2CLR, or Espressif. Firmware links point to their official sources.
- Flashing firmware carries a real risk of bricking the device if the wrong build variant is used (this project's own testing hit exactly that — recovered by reflashing the correct variant, documented as a reminder to check flash/PSRAM type before flashing, not as a guarantee it can't happen to you).
- Provided as-is, no warranty — see LICENSE.
- Receiving shortwave, amateur, and broadcast radio is legal in most jurisdictions for listening purposes, but rules vary by country. Check your local regulations, particularly around recording or publishing intercepted communications.
- The CW decoder is a best-effort tool, not a certified or professionally audited instrument. Don't rely on it for anything safety-critical.
MIT — see LICENSE.


