birdcage/CLAUDE.md
Ryan Malloy e0488eb85a Add Carryout G2 as fifth firmware variant with protocol support
Implement CarryoutG2Protocol based on cdavidson0522/winegard-sky-scan:
prompt-terminated reads via '>' char, 115200 baud RS-422, h <id>
motor homing, DVB/RSSI signal strength measurement. Update CLAUDE.md
with G2 variant column, NVS index 20, dvb sub-commands, and wiring
differences. CLI now accepts --firmware g2.
2026-02-11 10:06:58 -07:00

10 KiB

Winegard Trav'ler

Control a Winegard Trav'ler motorized satellite dish via RS-485 for amateur radio satellite tracking.

Project

  • Package: travler-rotor (installed via uv sync)
  • CLI entry point: travler-rotor (init / serve / pos / move)
  • Source layout: src/travler_rotor/ (src-layout)
  • Original upstream: Trav-ler-Rotor-For-HAL-2.05/ — Gabe Emerson's scripts, kept as reference (do not modify)

Build & Lint

uv sync                           # Install deps + package
uv run ruff check src/             # Lint
uv run ruff format --check src/    # Format check
uv run travler-rotor --help        # CLI smoke test

Architecture

protocol.py   — FirmwareProtocol ABC + HAL205Protocol / HAL000Protocol
                Serial I/O owned here. Each firmware version is a subclass.
leapfrog.py   — Pure function: apply_leapfrog(target, current) -> adjusted
                Predictive overshoot to compensate for mechanical motor lag.
antenna.py    — TravlerAntenna: high-level control wrapping protocol + leapfrog
                This is what consumers (CLI, rotctld, future MCP server) call.
rotctld.py    — RotctldServer: Hamlib rotctld TCP protocol (p/P/S/_/q)
                Bridges Gpredict to the antenna.
cli.py        — Click CLI with init/serve/pos/move subcommands

Firmware Variants

Five known Winegard dish variants documented by Gabe Emerson (KL1FI) / saveitforparts and cdavidson0522:

Detail HAL 0.0.00 HAL 2.05.003 Trav'ler Pro Carryout Carryout G2
Repo Travler_Rotor Trav-ler-Rotor-For-HAL-2.05 Travler-Pro-Rotor Carryout-Rotor winegard-sky-scan
Connection RS-485 / RJ-25 RS-485 / RJ-25 USB A-to-A (ttyACM0) RS-485 / RJ-25 RS-422 / RJ-12 6P6C
Baud rate 57600 57600 57600 57600 115200
Motor submenu mot motor odu then mot N/A (target + g) mot
Motor control a <id> <deg> a <id> <deg> a <id> <deg> g <az> <el> only a <id> <deg>
Search kill os -> kill Search ngsearch -> s -> q os -> kill Search N/A NVS 20 (permanent disable)
Boot signal NoGPS NoGPS or No LNB Voltage undocumented N/A undocumented
Min elevation 15 deg (firmware) 15 deg (firmware) 12 deg (firmware) 22 deg (firmware-enforced) 18 deg (firmware)
Max elevation 90 deg 90 deg 75 deg (hardware cap!) 73 deg (firmware default, NVS 102 override) 65 deg (firmware)
Position query a -> AZ = / EL = a -> AZ = / EL = a -> AZ = / EL = raw ints / 100 a -> floats
Tested model LG-2112 LG-2112 SK2DISH 2003 Carryout Carryout G2
HAL version 0.0.00 2.05.003 unknown 1.00.065 unknown
Prompt char > (likely) > (likely) undocumented undocumented > (confirmed)

Key Variant Differences

  • Trav'ler Pro odu command: The Pro's IDU has its own MCU. You must first tunnel to the ODU with odu before entering the motor submenu. The regular Trav'ler's IDU is a dumb RS-485 passthrough.
  • Carryout uses g not a: The Carryout has no individual motor addressing. It uses target to enter targeting mode, then g <az> <el> for combined moves. It also can't query its initial position.
  • Carryout has no limit switches: Uses motor stalling to detect mechanical boundaries (audible grinding).
  • Pro has the same leap-frog bug as the regular Trav'ler (copy-pasted).
  • NVS d command dumps all NVS values. Confirmed on Pro and Carryout G2; likely available on all variants.
  • Carryout DIP switches: All switches to off (up) may disable search mode, but behavior varies by unit.
  • Carryout G2 uses a not g: Unlike the 2003 Carryout, the G2 uses standard a <id> <deg> motor addressing and the mot submenu — protocol-compatible with the Trav'ler family.
  • Carryout G2 is RS-422 full-duplex: Separate TX/RX pairs at 115200 baud via RJ-12 6P6C, vs. RS-485 half-duplex at 57600 on the Trav'ler variants. Requires a USB-to-RS422 converter (5V TTL).
  • Carryout G2 has h <id> homing: Explicit motor home-to-reference command. Not documented on other variants.
  • Carryout G2 has DVB/RSSI: Signal strength measurement via dvb submenu (lnbdc odu to enable LNA, rssi <n> to sample). Used for sky scanning / RF imaging.

Hardware Specs (SK-1000)

Spec Value
Weight 45 lbs
Stow dimensions 10"H x 42"L x 26"W
Stow height 9.75"
Max deployed height 37" above mount
Arm reach 32.5" from base center
Dish size ~33" x 23" (Ku-band reflector)
Azimuth range 0-455 deg (before cable wrap)
Power input 120VAC -> 12VDC (RP-SK87 supply)
LNB bias 12-18VDC via coax
Motor type Likely stepper with gearing (unconfirmed)
Satellites (DISH) 110, 119, 129 (61.5 manual only)
Satellites (Bell) 82, 91

Hardware Protocol Notes

  • RS-485 serial, 57600 baud, 8N1 (via USB-to-RS232 + DTECH RS232-to-RS485 + RJ-25)
  • Motor commands: a <motor_id> <degrees> (0=azimuth, 1=elevation)
  • Position query: a (in motor submenu) returns AZ = <val> EL = <val> SK = <val>
  • Two motor control methods:
    • a <id> <deg> — queues command, waits for current motor to finish. Tolerant of rapid command streams from Gpredict.
    • g <az> <el> <sk> — immediate move, aborts on any new keystroke/character. Some firmware has a typo listing the order as az/sk/el.
  • Skew motor can run simultaneously with AZ or EL (the others are mutually exclusive)
  • Elevation floor: HAL 2.05 unreliable below 15 degrees with direct motor commands
  • Cable wrap limit: usually 360 or 455 degrees, dish reverses at limit
  • Console does not accept backspace — hit enter to clear on typo
  • Firmware prompt character is > (ASCII 62) — used for reliable response termination in prompt-terminated read strategies

RS-485 Pinout (RJ-25, bottom view, tip up)

Pin Function
1 GND
2 T/R-
3 T/R+
4 RXD-
5 RXD+
6 Not used

Firmware Console Commands

?              — list available commands
motor / mot    — enter motor submenu (firmware-dependent)
a              — show position (in motor submenu)
a <id> <deg>   — move motor to absolute position
h <id>         — home motor to reference position (G2, possibly others)
g <az> <el>    — go to AZ/EL (aborts on new input)
q              — exit current submenu
odu            — tunnel to outdoor unit (Trav'ler Pro only)
os             — enter OS submenu
  tasks        — list running tasks
  kill <name>  — kill a named task (e.g. "kill Search")
ngsearch       — enter search submenu (HAL 2.05 only)
  s            — stop search
nvs            — enter non-volatile storage submenu
  d            — dump all values (confirmed on Pro and G2)
  d <idx>      — dump single value with name/current/saved/default
  e <idx>      — read NVS value
  e <idx> <v>  — write NVS value
  s            — save changes
dvb            — signal info / LNB signal strength submenu
  lnbdc odu    — enable LNA in ODU mode (powers LNB for reception)
  rssi <n>     — read RSSI signal strength averaged over n samples
reboot         — reboot firmware
stow           — fold dish flat (caution: modified feeds may not survive)

Known NVS Indices

Index Setting
20 Disable tracker procedure (FALSE/TRUE)
102 Max elevation
125 Search minimum elevation
127 Safe minimum elevation

Error Messages

Message Meaning
AZ MOTOR STALLED Obstruction preventing rotation
EL MOTOR STALLED Obstruction preventing elevation change
EL Motor Home Failure Requires EL recalibration via IDU menu

IDU/ODU Cable Wiring (if cut)

Top row: Green, Yellow, Orange. Bottom row: Red, Brown, Black.

Power

120VAC input to RP-SK87 power supply, outputs 12VDC to IDU. Internal coax carries 12-18VDC bias for LNB — do not connect 5V equipment (SDR LNAs, etc.) without bypassing the power injector.

Physical Setup

  • Base marked with arrows and "BACK" at 0/360 deg (North)
  • Align "BACK" with true North for accurate tracking
  • Gpredict rotor config: 127.0.0.1:4533, 0->180->360 mode, min EL 15, max EL 90
  • No obstructions taller than 8" within 32.5" of base center

Calibration

On power-up, the dish performs calibration movements to establish position and cable wrap limits (~10-15 minutes on Carryout, shorter on Trav'ler). After calibration, firmware automatically starts a TV satellite search — the init sequence kills this.

Carryout uses motor stalling (not limit switches) to detect mechanical boundaries — expect audible grinding during calibration.

EL recalibration (via IDU buttons): POWER -> ENTER (hold 2s) -> User Menu -> INSTALLATION -> Calibrate EL -> confirm hard stop position.

Emergency Manual Stow

Last resort only. 5/16" socket + 6" extension into auxiliary drive hole. Turn clockwise slowly. Ensure arm faces "rear" label before lowering. Improper execution can damage motor.

Known Bugs (from upstream)

  • Leap-frog elevation bug: original travler_rotor.py lines 98-105 modify target_az instead of target_el. Fixed in leapfrog.py. Present in both Trav'ler and Trav'ler Pro repos. See docs/bugs.md.

Upstream References

  • github.com/saveitforparts/Travler_Rotor (HAL 0.0.00)
  • github.com/saveitforparts/Trav-ler-Rotor-For-HAL-2.05 (HAL 2.05)
  • github.com/saveitforparts/Travler-Pro-Rotor (Pro, USB)
  • github.com/saveitforparts/Carryout-Rotor (Carryout, HAL 1.00.065)
  • github.com/saveitforparts/Carryout-Radio-Telescope (RF scanning/imaging)
  • github.com/cdavidson0522/winegard-sky-scan (Carryout G2 sky scan + rotator)
  • Gabe Emerson / KL1FI — gabe@saveitforparts.com
  • YouTube: Trav'ler v1 demo (youtu.be/X1hnReHepFI), v2 demo (youtube.com/watch?v=URJZjo5EcpQ)

Testing

No hardware-in-the-loop tests yet. Protocol implementations can be mocked for unit testing — FirmwareProtocol is an ABC with clear method contracts.