Confirmed via DSD TECH SH-U11 RS-422 adapter at 115200 baud: - Firmware version 02.02.48, bootloader 1.01, Kinetis MCU, BCM4515 DVB - Position format is Angle[0]/Angle[1], not AZ=/EL= like Trav'ler - Prompts are TRK>/MOT>/NVS> (not bare >) - NVS 20 disable tracker confirmed working after power cycle - Full NVS dump captured (indices 0-143) - RJ-12 wire colors documented (differ from Davidson's guide) - RS-422 polarity swap symptoms documented (garbled RX vs silent TX) - Cable wrap range confirmed: -423.33° to +23.33° (446.66° total)
18 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 viauv 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 | Boot Complete then Loc Startup: IDU NOT Present |
| 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 | 02.02.48 |
| Prompt char | > (likely) |
> (likely) |
undocumented | undocumented | TRK> / MOT> / NVS> (confirmed) |
| Position format | AZ = / EL = |
AZ = / EL = |
AZ = / EL = |
raw ints / 100 | Angle[0] = / Angle[1] = |
| DVB tuner | unknown | unknown | unknown | unknown | BCM4515 (Broadcom) |
Key Variant Differences
- Trav'ler Pro
oducommand: The Pro's IDU has its own MCU. You must first tunnel to the ODU withodubefore entering the motor submenu. The regular Trav'ler's IDU is a dumb RS-485 passthrough. - Carryout uses
gnota: The Carryout has no individual motor addressing. It usestargetto enter targeting mode, theng <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
dcommand 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
anotg: Unlike the 2003 Carryout, the G2 uses standarda <id> <deg>motor addressing and themotsubmenu — 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. Tested with DSD TECH SH-U11 USB-to-RS422 adapter (FTDI FT232R). Polarity matters — A/B (or +/-) labeling is not standardized; if you get garbled data at the correct baud rate, swap the +/- wires on the RX pair. TX pair polarity swap causes the dish to not receive commands (silent failure).
- Carryout G2 position format differs from Trav'ler: Position query
ainMOT>submenu returnsAngle[0] = 180.00/Angle[1] = 45.00— not theAZ = / EL =format used by HAL 0.0.00 and HAL 2.05. TheCarryoutG2Protocolparser needs to handle this format. - Carryout G2 firmware version 02.02.48 confirmed (Copyright 2013 - Winegard Company). Bootloader version 1.01. MCU: Kinetis (NXP ARM Cortex-M). DVB tuner: BCM4515 (Broadcom).
- Carryout G2 boot sequence: Bootloader → SPI init → Motor init (System=12Inch, master=40000 steps, slave=24960 steps, ratio=1.602564) → DVB tuner init (BCM4515) → NVS load → EL home (stall detect, 2s timeout) → AZ home (stall detect, 8s timeout) →
Antenna Facing Front→TRK>prompt (if tracker disabled) or search start. - Carryout G2 cable wrap: Confirmed from homing output:
wrap_min:-42333 wrap_max:2333(centidegrees). Total range ~446.66°. - 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
dvbsubmenu (lnbdc oduto enable LNA,rssi <n>to sample). Used for sky scanning / RF imaging. Atmospheric baseline measured at boot: ~494 (18V) / ~499 (13V) wideband.
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) returnsAZ = <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 vs RS-422 — Serial Bus Differences
The Winegard variants use two different differential signaling standards. Understanding which one matters for choosing the right USB adapter.
RS-485 half-duplex (2-wire): One shared differential pair carries both TX and RX. Only one device talks at a time — the transmitter drives the bus, then releases it so the other side can respond. This is how the Trav'ler IDU communicates with the ODU: the IDU sends a command on the shared T/R pair, then listens for the response on the same wires. Simple wiring (2 signal wires + ground), but throughput is limited by the turn-around time between send and receive.
RS-422 full-duplex (4-wire): Two separate differential pairs — one dedicated TX pair and one dedicated RX pair. Both sides can transmit simultaneously because the signals don't share wires. Higher throughput (no bus turnaround penalty), and the Carryout G2 uses this at 115200 baud. Point-to-point only (one transmitter per pair).
RS-485 full-duplex (4-wire): Electrically identical to RS-422 wiring (same 4-wire differential pairs), but the RS-485 spec allows multiple transmitters on each pair (multi-drop bus). For our point-to-point dish↔computer connection, 4-wire RS-485 and RS-422 are interchangeable.
| Property | RS-485 half-duplex | RS-422 / RS-485 full-duplex |
|---|---|---|
| Signal wires | 2 (+ GND) | 4 (+ GND) |
| Direction | One direction at a time | Both directions simultaneously |
| Max nodes | 32 drivers + 32 receivers | 1 driver + 10 receivers (RS-422) |
| Max distance | 1200m / 4000ft | 1200m / 4000ft |
| Max baud | ~10 Mbps | ~10 Mbps |
| Voltage swing | ±1.5V to ±5V differential | ±2V to ±5V differential |
| Bus turnaround | Required (adds latency) | Not needed |
| Typical adapter | USB-to-RS485 (DTECH, etc.) | USB-to-RS422 (FTDI, DIYables, etc.) |
Practical consequence for this project: The Trav'ler's RJ-25 connector exposes both a half-duplex pair (pins 2-3, labeled T/R) and a dedicated receive pair (pins 4-5, labeled RXD). Gabe's code uses only the half-duplex pair via an RS-485 adapter. Davidson's G2 code uses all four wires as RS-422. The same physical connector may support both modes depending on the firmware — this is unconfirmed on the Trav'ler but worth testing if you have a 4-wire adapter available.
Serial Connector Pinout
The physical connector is an RJ-25 (6P6C) on the Trav'ler or RJ-12 (6P6C) on the G2 — same form factor, same 6-pin modular jack.
Trav'ler pinout (RJ-25, bottom view, clip up):
| Pin | Label | RS-485 use | RS-422 use |
|---|---|---|---|
| 1 | GND | Ground | Ground |
| 2 | T/R- | Shared data- | TX- (computer→dish) |
| 3 | T/R+ | Shared data+ | TX+ (computer→dish) |
| 4 | RXD- | (unused in half-duplex) | RX- (dish→computer) |
| 5 | RXD+ | (unused in half-duplex) | RX+ (dish→computer) |
| 6 | N/C | Not connected | Not connected |
Carryout G2 pinout (RJ-12, clip away, per Davidson's wiring guide):
| Pin | Wire Color (Davidson) | Wire Color (confirmed) | RS-422 Function |
|---|---|---|---|
| 1 | White | Orange/White | GND (PE) |
| 2 | Red | Orange | TX+ (TA) — computer→dish |
| 3 | Black | Green/White | TX- (TB) — computer→dish |
| 4 | Yellow | Blue | RX+ (RA) — dish→computer |
| 5 | Green | Blue/White | RX- (RB) — dish→computer |
| 6 | Blue | Green | Not connected |
Note: Wire colors vary by cable manufacturer. The "confirmed" column is from a standard 6P6C flat cable tested 2026-02-12. Always verify with a multimeter before connecting. Polarity is critical: swapping +/- on the RX pair produces garbled data at the correct baud rate (systematic bit inversion, not random noise). Swapping +/- on the TX pair causes silent failure (dish doesn't respond because it can't decode the inverted framing).
Adapter chain by variant:
| Variant | Adapter | Wires Used |
|---|---|---|
| Trav'ler (Gabe's setup) | USB→RS232→RS485 (DTECH) | Pins 2-3 only (half-duplex) |
| Carryout G2 (Davidson) | USB→RS422 (5V TTL) | Pins 2-5 (full-duplex) |
| Carryout G2 (confirmed) | DSD TECH SH-U11 USB→RS422 (FTDI FT232R) | Pins 1-5 (full-duplex + GND) |
| Carryout G2 (ESP32) | ESP32 UART2→RS422 module (DIYables) | Pins 2-5 (full-duplex) |
RS-422 Module Notes (DIYables MAX490)
The DIYables RS422-to-TTL module uses the MAX490 transceiver chip (2.5 Mbps max, well above our 115200 baud). Key specs:
- 5V TTL logic on the microcontroller side (RXD/TXD)
- 15 kV ESD protection on RS-422 lines
- TVS diode for lightning/spike suppression
- 10 ohm current-limiting resistors for overcurrent protection
- Built-in 120 ohm termination resistor (reduces echo on long runs)
- Power + TX/RX activity LEDs
- Board size: 5.0cm x 2.7cm
Failsafe concern: The MAX490 does not have failsafe logic, and the module has no provisions for passive failsafe bias resistors. When the RS-422 bus tri-states (no driver active — e.g., between commands, during power transitions, or if the dish firmware is slow to respond), the receiver inputs float and may see random transitions interpreted as garbage data. This can cause spurious bytes in the serial stream.
Workaround options:
- Add external bias resistors — pull A/RX+ toward V+ and B/RX- toward GND through ~560 ohm resistors. This biases the idle bus to a known logic-high state (RS-422 "mark" / idle). Solder to the module or add inline on the RJ-12 breakout.
- Use the prompt-terminated read strategy — our
CarryoutG2Protocol._send()reads until>(ASCII 62) which naturally filters out garbage between commands, since random transitions are unlikely to produce a valid>in context. - Ignore idle noise in firmware — the Winegard firmware likely ignores unexpected input while it's processing or idle, but any bytes received during the bus float could corrupt the next valid command if they land in the UART buffer at the wrong time.
For short cable runs (under ~3m between ESP32 and dish), the built-in 120 ohm termination is sufficient and bus float is less likely to cause issues. For longer runs or electrically noisy environments (near motors, power supplies), add the bias resistors.
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
Full dump in docs/g2-nvs-dump.md (firmware 02.02.48, captured 2026-02-12).
| Index | Setting | Default | Notes |
|---|---|---|---|
| 20 | Disable Tracker Proc? | FALSE | Set TRUE to prevent TV satellite search on boot |
| 38 | Sleep Mode Timer Secs | 420 | 7 minutes before sleep |
| 41 | Satellite Scan Velocity | 55.00 | °/s during TV search |
| 80 | AZ Max Vel | 65.00 | °/s azimuth max velocity |
| 81 | AZ Max Accel | 400.00 | °/s² azimuth max acceleration |
| 83 | AZ Steps/Rev | 40000 | Stepper steps per full rotation |
| 85 | EL Max Vel | 45.00 | °/s elevation max velocity |
| 88 | EL Steps/Rev | 24960 | Stepper steps per full EL rotation |
| 101 | Minimum Elevation Angle | 18.00 | Firmware floor (degrees) |
| 102 | Maximum Elevation Angle | 65.00 | Firmware ceiling (degrees) |
| 103 | Elevation Home Angle | 65.00 | EL position after homing |
| 112 | Disable Dipswitch? | FALSE | Override physical DIP switches |
| 113 | Dipswitch Value | 101 | DirecTV config (ignored when tracker disabled) |
| 128-133 | AZ/EL PID Gains | varies | Kp/Kv/Ki tuning parameters |
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.pylines 98-105 modifytarget_azinstead oftarget_el. Fixed inleapfrog.py. Present in both Trav'ler and Trav'ler Pro repos. Seedocs/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.