A proxy server for the Xiegu G90 HF transceiver that bridges the radio's CI-V serial interface to multiple network protocols simultaneously.
Connect WSJT-X, JTDX, JS8Call, hamlib, and custom scripts — all at the same time, to the same radio.
Disclaimer: This project is not affiliated with, endorsed by, or connected to Xiegu or any of its subsidiaries. I am just a ham radio operator who needed to control my G90 headless from a remote machine, and built this tool to scratch my own itch.
Successfully tested with:
- Xiegu G90 firmware 1.81
- WSJT-X via XML-RPC (flrig emulation on port 12345)
- rigctl (Hamlib command-line client) via TCP port 4532 — all read/set commands for frequency, mode, VFO, PTT, and signal strength confirmed working
The G90 has a single serial port (CI-V). g90proxy opens that port and exposes the radio over three protocols in parallel:
| Protocol | Default Port | Compatible with |
|---|---|---|
| HTTP REST + SSE | 8080 |
Custom scripts, web dashboards |
| Hamlib TCP (rigctld) | 4532 |
WSJT-X, JTDX, JS8Call (Hamlib mode), rigctl |
| XML-RPC (flrig) | 12345 |
WSJT-X, JTDX, JS8Call (flrig mode), fldigi |
All requests are serialized internally — only one CI-V command reaches the serial port at a time, preventing collisions between clients.
- Read / set frequency (Hz)
- Read / set mode: LSB, USB, AM, CW, CW-R, FM, NFM, PKTLSB (L-D), PKTUSB (U-D)
- Read / set PTT (TX/RX)
- Read / set VFO (A/B)
- Read / set TX power (1–20 W)
- Read S-Meter, SWR, power output (real-time)
Digital modes U-D and L-D (firmware 1.79+) are fully supported via CI-V command 0x26.
- Xiegu G90 with firmware 1.79+ (for digital mode support)
- USB-to-serial adapter connected to the G90 CI-V port
- Go 1.21+
git clone https://github.com/logocomune/go-g90proxy
cd go-g90proxyLinux (amd64)
GOOS=linux GOARCH=amd64 go build -o g90proxy ./cmd/g90proxymacOS — Intel
GOOS=darwin GOARCH=amd64 go build -o g90proxy ./cmd/g90proxymacOS — Apple Silicon (M1/M2/M3)
GOOS=darwin GOARCH=arm64 go build -o g90proxy ./cmd/g90proxyRaspberry Pi (32-bit, Pi 2/3/4)
GOOS=linux GOARCH=arm GOARM=7 go build -o g90proxy ./cmd/g90proxyRaspberry Pi (64-bit, Pi 3/4/5)
GOOS=linux GOARCH=arm64 go build -o g90proxy ./cmd/g90proxyWindows (amd64)
$env:GOOS="windows"; $env:GOARCH="amd64"; go build -o g90proxy.exe ./cmd/g90proxyPre-built multi-platform images (linux/amd64, linux/arm64) are published to the GitHub Container Registry on every release:
| Image | Tag | Description |
|---|---|---|
ghcr.io/logocomune/g90proxy |
latest / vX.Y.Z |
Proxy server |
ghcr.io/logocomune/g90proxy/tui |
latest / vX.Y.Z |
Terminal UI |
Pull and run the proxy server:
docker pull ghcr.io/logocomune/g90proxy:latest
docker run --rm \
--device /dev/ttyUSB0 \
-e HAMPROXY_SERIAL_PORT=/dev/ttyUSB0 \
-p 4532:4532 -p 8080:8080 -p 12345:12345 \
ghcr.io/logocomune/g90proxy:latestRun the TUI:
docker run -it --rm \
-e HAMPROXY_SERVER=host.docker.internal:4532 \
ghcr.io/logocomune/g90proxy/tui:latestNote: If you get the error
could not open a new TTY, ensure you are using the-itflags. The TUI requires an interactive terminal to function properly inside Docker.
Build locally from source:
# Proxy server
docker build -t g90proxy:latest .
# TUI
docker build -f Dockerfile.tui -t g90proxy-tui:latest .
# Multi-platform
docker buildx build --platform linux/amd64,linux/arm64 -t g90proxy:latest ../g90proxyThe serial port defaults to /dev/serial/by-id/usb-FTDI_USB__-__Serial-if00-port0. Override with the HAMPROXY_SERIAL_PORT environment variable or command-line flags.
./g90proxy --help| Variable | Default | Description |
|---|---|---|
HAMPROXY_SERIAL_PORT |
/dev/serial/by-id/... |
Serial port path |
HAMPROXY_SERIAL_POLL_INTERVAL |
1s |
Status polling interval |
HAMPROXY_TCP_LISTEN |
:4532 |
Hamlib TCP listen address |
HAMPROXY_HTTP_LISTEN |
:8080 |
HTTP listen address |
HAMPROXY_XMLRPC_LISTEN |
:12345 |
XML-RPC listen address |
HAMPROXY_XMLRPC_DIGITAL_STICKY |
false |
Set to true to keep digital mode when WSJT-X sends USB/LSB (see Digital Sticky) |
G90_DEBUG |
0 |
Set to 1 to log raw CI-V frames |
WSJT-X (and similar software) can send a plain USB or LSB mode request when connecting, even if the radio was manually set to U-D or L-D. This causes the radio to silently leave digital mode.
When HAMPROXY_XMLRPC_DIGITAL_STICKY=true, the XML-RPC server intercepts USB/LSB set requests and promotes them to USBD/LSBD only if the radio is already in a digital mode (PKTUSB or PKTLSB). Any other mode (AM, CW, FM…) passes through unchanged.
HAMPROXY_XMLRPC_DIGITAL_STICKY=true ./g90proxyThis option only affects the XML-RPC (flrig) interface. The Hamlib TCP and HTTP interfaces are not affected.
- WSJT-X / JTDX / JS8Call:
- Open Settings -> Radio.
- Select Rig:
flrig. - In flrig server:
localhostport12345. - Click Test CAT.
- Hamlib / rigctl:
- Use the generic TCP client:
rigctl -m 2 -r localhost:4532.
- Use the generic TCP client:
If you get a "permission denied" error when opening the serial port, ensure your user has access to the dialout group:
sudo usermod -a -G dialout $USERNote: You may need to log out and back in for the changes to take effect.
A terminal UI is available for local monitoring and control:
go build -o g90proxy-tui ./cmd/g90proxy-tui
./g90proxy-tuiKeys: u/d frequency step, m cycle mode, v toggle VFO, t toggle TX, s step size, q quit.
docs/http.md— HTTP REST API and SSE streamdocs/hamlib.md— Hamlib TCP protocol and supported commandsdocs/xmlrpc.md— XML-RPC (flrig) protocol and supported methodsdocs/serial.md— CI-V protocol details and digital mode implementation
This project is licensed under the GNU General Public License v3.0. See LICENSE for details.
THIS SOFTWARE IS PROVIDED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN RISK. SEE THE GNU GENERAL PUBLIC LICENSE FOR MORE DETAILS.
Controlling a radio transmitter via software carries inherent risks. Always ensure you comply with your local regulations and that the radio is operated safely.
