Remote Control Your Ham Radio From an iPhone

What it is

HamPal puts your shack in your pocket. It's a native iOS app (with a companion macOS build) that lets you operate an amateur radio transceiver from anywhere — across the room, across the house, or across the world over a VPN. You get full control over frequency, mode, PTT, audio in and out, and most of the front-panel controls you'd reach for on the radio itself, all from a clean SwiftUI interface designed for one-handed use.

If you've ever wanted to sit on the couch and chase DX while the rig stays in the shack, work a satellite pass from the back garden, or run a portable HF station from a hotel room while the radio sits at home, this is what HamPal is for.

Quick Start Guide

The Quick Start Guide now has its own page, here:

Ham Pal: Quick Start Guide
Already familiar with remote operation and just want the cookbook? Here’s the short version. Skip to the per-radio sections further down if any of these steps need more context. Yaesu FT-991A 1. Plug the radio into a Windows PC over USB. CAT settings on the radio: menu 031 = 38400, 032

What it supports

Radio How it connects Power Bands
Yaesu FT-991A Through a Windows PC physically connected to the radio over USB 100 W HF/6m, 50 W 2m/70cm HF / 6m / 2m / 70cm
ICOM IC-9700 Directly to the radio's built-in LAN port 100 W 2m, 75 W 70cm, 10 W 23cm 2m / 70cm / 23cm
ICOM IC-7100 Through a Raspberry Pi physically connected to the radio over USB 100 W HF/6m, 50 W 2m, 35 W 70cm HF / 6m / 2m / 70cm

Three different radios, three different connection styles, but the iOS app feels the same regardless of which one is on the other end. The setup screen even hides controls the rig doesn't have, so you only see what's actually relevant to your station.

How HamPal works (the 60-second version)

There are two halves to HamPal: the iOS app that you tap and hold and slide on, and a transport that gets your taps and your voice to the radio. The transport varies by rig:

  • The IC-9700 has an Ethernet jack and a built-in network server — HamPal talks to it directly using ICOM's RS-BA1-compatible protocol over UDP. Nothing else in the loop.
  • The FT-991A has neither LAN nor a built-in server. It needs a small Windows host program (downloadable as a single .exe — see the FT-991A section below) running on a PC connected to the radio over USB. The PC handles CAT control over the virtual COM port and the USB audio codec for RX/TX audio, and re-exposes both on the network so the iOS app can reach them.
  • The IC-7100 is in the same boat — no LAN port, USB-only — but its bridge is a small Python daemon designed to run on a Raspberry Pi (or any cheap Linux box). The Pi owns the radio's serial port and audio codec, and exposes them on the network using the same wire protocol the Windows host uses for the FT-991A.

In all three cases, audio is 48 kHz mono (Opus-compressed for the FT-991A and IC-7100, native LPCM for the IC-9700), and control commands round-trip in well under 100 ms on a healthy LAN. Over a VPN like Tailscale or WireGuard you can comfortably operate from anywhere with usable internet.

Getting started

You'll need an iPhone running iOS 17 or later, a working iCloud account (only used so the build signs to your device), and physical access to your radio for the initial setup. After that, day-to-day operation only needs network reach to the host or radio.

Common to all radios

  1. Make sure your phone and your radio (or its host PC / Pi) are on the same network. If you want to operate from outside the house, set up a VPN. Tailscale is the easiest path for most operators — install it on the host machine and on your phone, and "the same network" magically follows you everywhere.
  2. Open the HamPal app. The first screen is the radio picker — choose your model.
  3. Read the on-screen setup instructions. They're tailored to whichever radio you picked.
  4. When you're ready, enter the IP address or hostname of your radio (or its host) and tap Connect.

The hard part is setting up the bridge or host program for your specific radio. The next three sections walk through each one.


Yaesu FT-991A — Windows host setup

The FT-991A connects to a Windows PC over USB. The PC presents the radio as both a virtual COM port (for CAT commands) and a USB audio codec (for RX/TX audio). HamPal's Windows host program bridges those to the network.

You'll need

  • A Windows 10 or 11 PC that can stay running while you operate
  • The radio's USB cable
  • The Yaesu USB driver (SCU-17 / Silicon Labs CP210x — Yaesu's website has it under "FT-991A files")

On the radio, set the following menu items so the host can talk to it cleanly:

Menu Setting Value
031 CAT RATE 38400
032 CAT TOT 100 msec
033 CAT RTS ENABLE

(38400 is the recommended rate; the host will also auto-detect 4800/9600/19200 if you've changed it.)

Install the host

Download the latest build:

https://www.k3mgm.com/downloads/hampal-host/latest.zip

Unzip it anywhere convenient (Documents is fine) and double-click HamPalHost.exe. That's it — the .NET runtime is bundled into the .exe, so there's nothing else to install.

The download always tracks the current main branch — every commit produces a fresh build automatically. If you want a tagged release version instead of the rolling build, the Releases page has the same zip attached to each tag.

The first time you launch the .exe, Windows SmartScreen will probably show "Windows protected your PC" because the binary isn't code-signed. Click More info → Run anyway. (Code signing is on the long-term wishlist; until then, this warning is expected for any unsigned hobby binary.)

On first launch the host scans every COM port on the system for an FT-991A and remembers the one it finds in %APPDATA%\HamPalHost\settings.json. Subsequent launches re-use that port directly. If Windows ever reassigns COM numbers (common after unplugging and replugging the USB cable), the host detects the old port no longer responds and falls back to a fresh scan automatically.

Command-line options (all optional):

HamPalHost                — use the last successful COM port (or auto-detect)
HamPalHost --auto-detect  — force a fresh scan, ignore the saved port
HamPalHost --port COM4    — pin a specific port (overrides everything)
HamPalHost --baud 9600    — override the baud rate
HamPalHost --list-audio   — list available audio devices and exit
HamPalHost --help         — show all options

If the host can't find the audio codec, run HamPalHost --list-audio and pass a substring of the right one with --audio "CODEC". Most installations work with the default.

When the host is running you'll see it start listening on TCP 7300 (control) and UDP 7301 (audio). Make a note of the PC's IP address (ipconfig from any cmd window). That's what you'll type into the iOS app.

Building from source (only needed if you want to modify the host yourself):

cd windows/RemoteRadioHost
dotnet run

This needs the .NET 8 SDK installed.

Going to production: the host will keep running for as long as the cmd window is open. For a permanent setup, create a Windows scheduled task that runs HamPalHost.exe at startup, or wrap it in a Windows service via NSSM.

Network ports to keep open (only relevant if you have a firewall between your phone and the PC):

Port Protocol Purpose
7300 TCP Control commands
7301 UDP Bidirectional audio

ICOM IC-9700 — direct LAN setup

The IC-9700 is the easy one. It has an Ethernet jack right on the back, runs its own network server, and HamPal talks to it directly. No PC, no Pi, nothing in between.

You'll need

  • An Ethernet cable from the IC-9700 to your network
  • The radio's username and password (you set these on the radio)

On the radio, enable network control:

  1. Menu > SET > Network — Enable LAN.
  2. Menu > SET > Network > Remote Control — Set a username and password. Pick something you'll remember; you'll type both into HamPal once.
  3. Note down the IP address the radio gets from your DHCP server — it's shown on the same network screen. (You can also reserve a static lease in your router so it doesn't drift.)

In HamPal, choose IC-9700 on the picker, open Settings, and enter the username and password under the ICOM credentials section. Then enter the radio's IP address on the connect screen and tap Connect.

The IC-9700 uses three UDP ports for its control + CI-V + audio streams. If you have a firewall in the way, allow traffic from your phone to ports 50001-50003 on the radio's IP.

Network ports:

Port Protocol Purpose
50001 UDP Session control and authentication
50002 UDP CI-V commands
50003 UDP Bidirectional audio

ICOM IC-7100 — Raspberry Pi bridge setup

The IC-7100 doesn't have a LAN port, but it does have a USB-B jack that exposes both a virtual COM port (for CI-V commands) and a USB audio codec. HamPal uses a small Python daemon — the "Pi bridge" — running on a Raspberry Pi physically connected to the radio's USB port. The Pi owns the serial port and the audio codec, and re-exposes them on the network using the same protocol the FT-991A's Windows host uses, just on different ports.

You don't strictly need a Raspberry Pi — any Linux box will work — but a Pi 3B+ or newer is more than enough horsepower, costs about as much as a decent dinner, and runs forever. If you've got an old one in a drawer, that's the right tool for this job.

You'll need

  • A Raspberry Pi (or any Linux box) running a recent Raspberry Pi OS / Debian / Ubuntu
  • The radio's USB cable
  • The HamPal source from this repository, copied or cloned to the Pi

On the radio, set the following in Menu > SET > Connectors:

Item Value
CI-V Baud Rate 19200
CI-V Address 88h (default)
CI-V Transceive ON
USB SEND OFF (PTT goes via CI-V, not RTS/DTR)
USB MOD Level adjust for clean modulation

On the Pi, install the dependencies once:

sudo apt update
sudo apt install python3-pip libopus0 portaudio19-dev
cd ham-pal/pi-bridge/ic7100
pip3 install -r requirements.txt

Plug the radio into the Pi over USB and check which serial device it came up as — usually /dev/ttyUSB0. You can confirm with dmesg | tail right after plugging it in.

Then run the bridge:

python3 bridge.py --serial /dev/ttyUSB0

If the bridge can't find the right audio device (the IC-7100 usually shows up as "USB Audio CODEC"), run python3 bridge.py --list-audio to see what's available and pass it explicitly:

python3 bridge.py --serial /dev/ttyUSB0 \
  --capture-device "USB Audio CODEC" \
  --playback-device "USB Audio CODEC"

When the bridge is running you'll see it start listening on TCP 7400 (control) and UDP 7401 (audio). The Pi's IP address goes into HamPal.

Going to production: drop the bridge into a systemd unit so it survives reboots. There's a copy-paste-ready unit file in pi-bridge/ic7100/README.md. After enabling it, the radio is reachable from your phone any time the Pi is powered on — you can leave the rig set up and forget about it.

Network ports:

Port Protocol Purpose
7400 TCP Control commands
7401 UDP Bidirectional audio

Operating tour: what's in the app

HamPal's main screen shows everything important above the fold. From the top:

  • Frequency display — large, readable, with +/− nudge buttons and a configurable step size (1 kHz to 1 MHz on most rigs). Tap the digits to type in a frequency directly.
  • Mode selector — quick-tap buttons for the modes you actually use (FM, USB, LSB, CW), and a menu for the rest (DATA-U, FM-N, C4FM on Yaesu; DV, DD, RTTY on ICOM, etc.).
  • Band quick-select — one tap to jump to a band. The buttons are tailored to the radio you're connected to.
  • Memory channel recall — the radio's stored memory channels are listed by tag and frequency for one-tap recall (FT-991A and IC-9700; not yet implemented on the IC-7100 bridge).
  • S-meter — calibrated S-unit display that's accurate per-radio. You see real S-units, not raw counts.
  • Squelch with slider and +/− buttons.
  • TX power — capped automatically at the band-appropriate maximum so you can't accidentally dial in 100 W on 70 cm and damage the rig.
  • PTT — push and hold to transmit, with a haptic kick on press and release. There's a PTT lock if you'd rather toggle than hold.
  • Noise blanker, noise reduction, AGC — toggles for each (when the radio supports them).
  • VFO A/B swap, scan start/stop, radio power on/off, split/repeater offset display.

Radio Settings (FT-991A only). When you're connected to an FT-991A, a "Settings" tile with a radio icon appears at the end of the square toolbar row (next to ATU/Tune). Tap it and HamPal pulls every one of the FT-991A's 154 menu items off the radio over CAT and groups them by function (AGC, Display, Keyer, CW Mode, SSB Mode, Repeater, VOX, TX Power, …) — the same partitioning the operating manual uses. Enumerated items (e.g. CAT Rate, Display Color, Tuner Select) render as native pickers showing the human values you'd see on the radio's own screen ("9600 bps", "BLUE", "INTERNAL"). Numeric and free-form items render as text fields, with the manual's range as a hint. Numeric items are clamped and snapped to the radio's required step on submit (the FT-991A silently drops out-of-step writes, so HamPal enforces the rule before sending). Read-only items (Radio ID) display as plain text. Changes apply optimistically and the row shows a small spinner that resolves to a green check (verified against the radio) or a red triangle (write rejected — row reverts). Note this is the radio's settings, not the HamPal app's settings — the gear icon at the top still opens the app settings. The tile is hidden for other radios because each rig's menu surface is different.

Backup and restore. The Radio Settings popover has a … menu in the top-right with Backup to Files… and Restore from File…. Backup serializes the entire current snapshot to a pretty-printed JSON file (named ft991a-settings-YYYY-MM-DD-HHMM.json by default) and hands it to the iOS Files picker so you can drop it into iCloud Drive, On My iPhone, or any other location. Restore reads a previously-saved JSON file, refuses to apply backups from a different radio model, computes the diff against the radio's current settings, and shows a confirmation with the count of changes ("Restore 47 Settings"). On confirm it walks the diff sequentially with the same per-row optimistic-write feedback as manual edits, so you can watch each row update with a spinner that turns green (or red, if the radio rejects something). Read-only items and items not present in the current catalog are skipped automatically.

The "less common but still nice to have" controls live behind a settings drawer:

  • RX/TX audio recording — hit record and the audio gets captured as a WAV file you can share or AirDrop off the device.
  • Contact logger — log a callsign with frequency, signal report, and notes. Useful for casual rag-chew bookkeeping; not a replacement for a "real" log if you chase awards.
  • DTMF pad — for repeater autopatch and link control.
  • Screen lock and PTT lock — keeps the display awake during a long QSO and prevents the PTT button from firing if your hand brushes the screen.
  • Auto-reconnect — if the network blips, HamPal tries to reconnect on a backoff. You can yank Wi-Fi mid-QSO and it'll come back as soon as the network does. (See the limitations section below — auto-reconnect on the IC-9700 and IC-7100 paths is more limited than on the FT-991A.)
  • Background audio + Now Playing — the audio keeps flowing when you switch apps, and the lock screen / car display shows the current frequency and mode.
  • Light/dark/system appearance and audio device picker.

Things to be aware of

Network trust. The Windows host (FT-991A) and the Pi bridge (IC-7100) don't authenticate clients — anyone who can reach the host's TCP control port can take over the radio. This is fine on a home LAN or behind a VPN, but don't expose those ports to the open internet. If you want remote operation, put the host behind Tailscale, WireGuard, or a similar VPN. The IC-9700 has its own username/password mechanism and is safer on a hostile network, but I'd still tunnel it.

One client at a time. All three transports allow exactly one connected client. If you connect a second device, the first one gets booted with a polite error message. This is intentional — it keeps PTT and audio state from getting tangled.

TX power limits are a soft cap, not a hardware limit. HamPal's TX power slider tops out at the band-appropriate maximum, but the radio itself enforces nothing — if you put a value through some other tool that exceeds your antenna or finals can handle, the radio will happily transmit at it. Don't override the cap.

Latency over the internet. On a LAN, audio latency is around 30-50 ms each way and totally usable for SSB or FM rag-chew. Over a VPN to a remote site you're typically looking at 80-200 ms one-way depending on the route, which is fine for voice but feels sluggish for CW QSK. If you do CW, run the keyer on the radio side and use the iOS keyer to feed text, not paddle elements.

Audio quality. RX audio is full-quality 48 kHz mono. The Opus codec used for the FT-991A and IC-7100 paths is bandwidth-efficient and copes well with packet loss; the IC-9700's native LPCM is higher quality but uses more bandwidth. Either way, you can expect noticeably better audio than a typical phone-patch link.

Reconnect on the ICOM rigs. The auto-reconnect logic was originally written for the FT-991A path and currently only re-dials those endpoints. If your IC-9700 or IC-7100 connection drops, you'll need to tap Connect again manually. It's on the list to fix.

Things the IC-7100 bridge doesn't do yet:

  • D-STAR DV voice (the audio path is plain PCM/Opus only)
  • Memory channel readout
  • Hardware paddle keying via the iOS app
  • ATU control, repeater shift programming, CTCSS/DCS programming

The iOS UI hides most of these via the IC-7100 profile's feature flags, so you won't see broken buttons — they just don't appear.

Troubleshooting

"Connection refused" — the host or bridge isn't running, or it's listening on a different port. Check the Windows host / Pi bridge console for "listening on port..." messages. On Windows, also check that the Windows Firewall isn't blocking the host program — Windows usually pops a dialog the first time you run it; click "Allow access on private networks."

"Hostname not found" — typo in the IP, or you're on a different network (or VPN) than the host. Try pinging the host's IP from another device on the same network.

Audio works one way but not the other — almost always a microphone-permissions issue on iOS (Settings > HamPal > Microphone) or an audio device picked wrong on the host side. On Windows, run the host with --list-audio and check that the radio's USB codec is one of the listed devices. On the Pi, same thing with python3 bridge.py --list-audio.

Frequency / mode commands don't take effect on the FT-991A — usually a CAT settings problem on the radio. Double-check menus 031 / 032 / 033 against the table above.

PTT works but no modulation on the IC-7100 — check that USB SEND is set to OFF in the radio's connector menu. HamPal sends PTT via CI-V, not via the USB serial RTS/DTR line. If USB SEND is set to anything other than OFF, the radio's expecting a hardware key signal you're not giving it.

Static / pops / dropouts on audio — almost always network jitter. Try moving the host or radio onto wired Ethernet rather than Wi-Fi, especially the host side. If you're on Wi-Fi at the phone end and the network is good, the Opus codec will paper over a fair amount.

"Another client connected, you have been disconnected" — exactly what it says. Someone else (or another device of yours) connected to the host. Only one client at a time.

73 and have fun.