Rewrote hidden_menu_probe.py from Winegard-hardcoded to auto-discovering: detects prompt, error string, and submenu structure from any firmware console. Extracted Winegard-specific candidate words to scripts/wordlists/winegard.txt. Deep probe of all 12 G2 submenus discovered commands across A3981 (driver diagnostics), ADC (RSSI monitoring + position sweep), DVB (extended help via man, transponder selection), EEPROM (read/write), GPIO (pin R/W), LATLON (calculator), MOT (azscan, sw), PEAK (EchoStar switch), and STEP (raw stepper control). NVS submenu generates false positives — treats any input as sequential index reads. Safety: added q/Q to default blocklist, bare-CR check before navigate_to_root to prevent accidental shell termination between submenus.
67 KiB
Carryout G2 Firmware Exploration
Firmware: Version 02.02.48 (Copyright 2013 - Winegard Company) Date: 2026-02-12 Connection: DSD TECH SH-U11 USB RS-422 @ 115200 8N1
Hardware Platform
Discovered via os → id command:
NXP Kinetis K60 ARM Cortex-M4
Package: 144-pin
Silicon Rev: 2.4
Mask Set: 4N22D
P-Flash: 512 KB
RAM: 128 KB
Core Clock: 96 MHz (CCLK)
Bus Clock: 48 MHz (BCLK)
System ID: TWELINCH
Antenna ID: 12-IN G2
Software: 02.02.48
Flash Base: 0x00010000 (65536)
Flash Size: 458752 bytes (448 KB)
The "TWELINCH" system ID = "Twelve Inch", matching the Carryout G2's ~12" dish diameter. Flash starts at 64 KB offset (first 64 KB is bootloader/vector table), leaving 448 KB for application firmware.
Exact part number: MK60DN512VLQ10
- MK60 = Kinetis K60 family (Cortex-M4 + DSP)
- DN512 = 512 KB program flash (no FlexNVM)
- VLQ = LQFP 144-pin package (20x20mm)
- 10 = 100 MHz max frequency
Datasheets: docs/K60-datasheet.pdf (K60P144M100SF2V2, Rev 3, 6/2013),
docs/K60-reference-manual.pdf (K60P144M100SF2V2RM, ~1800 pages)
Key Peripherals (from datasheet)
| Peripheral | Count | Notes |
|---|---|---|
| UART | 5 | UART0 = RS-422 console (confirmed) |
| DSPI | 3 | SPI with DMA; DSPI0/1 likely → A3981 motor drivers |
| I2C | 2 | |
| ADC | 2× 16-bit | 863ns conversion; ADC0 likely → RSSI measurement |
| DAC | 2× 12-bit | |
| USB | 1× OTG | On-chip transceiver, no external PHY needed |
| CAN | 2 | Likely unused |
| Ethernet | 1× IEEE 1588 | Likely unused |
| FlexTimer | 3 (12 ch) | Motor PWM / step timing |
| DMA | 16 channel |
USB Port (Potentially Accessible)
The K60 has dedicated USB pins (not muxable with GPIO):
| LQFP Pin | Signal | Function |
|---|---|---|
| 19 | USB0_DP | USB Data+ |
| 20 | USB0_DM | USB Data- |
| 21 | VOUT33 | USB VREG 3.3V output |
| 22 | VREGIN | USB VREG 5V input (self-power from USB) |
The Trav'ler Pro uses USB A-to-A (ttyACM0) for its serial console — this
proves Winegard has USB CDC/ACM firmware for the Kinetis platform. The G2 may
also have a USB connector on the PCB (possibly internal, for field service).
NVS indices 2 ("Debug 2nd Console Port") and 4 ("Debug Port Connection") hint at multiple console port support — USB could be the second port.
Root Menu Structure
At the TRK> prompt, ? lists all available submenus:
| Submenu | Command | Description |
|---|---|---|
| A3981 | a3981 |
Allegro A3981 stepper motor driver IC control |
| ADC | adc |
Analog-to-digital converter / RSSI / board ID |
| Dipswitch | dipswitch |
DIP switch configuration readout |
| DVB | dvb |
BCM4515 DVB receiver / signal analysis |
| EEPROM | eeprom |
Non-volatile EEPROM storage (separate from NVS) |
| GPIO | gpio |
MCU pin register dump (5 ports, 98 pins) |
| LATLON | latlon |
Satellite longitude/elevation parameters |
| MOT | mot |
Degree-based motor positioning (high-level) |
| NVS | nvs |
Non-volatile settings (operational parameters) |
| OS | os |
Operating system / task manager / MCU identification |
| PEAK | peak |
Signal peaking / DiSEqC switch testing |
| STEP | step |
Raw microstep motor control (low-level) |
Three-layer motor control architecture:
step— raw microstep commands (ustep/sec, engage/release motors)mot— degree-based positioning (a <id> <deg>,h <id>)- Application — satellite tracking (NVS config, peak, DVB)
NVS Values
Num Name Current Saved Default
---- -------------------------- ---------- ---------- ----------
0) Log ID's 0x00000007 0x00000007 0x00000007
1) Log Device 0x00000001 0x00000001 0x00000001
2) Debug 2nd Console Port 0 0 0
3) Debug 2nd Packet Port 0 0 0
4) Debug Port Connection 0 0 0
16) Pitch Deadband 0.00 0.00 0.00
17) Roll Deadband 0.00 0.00 0.00
18) Yaw Deadband 0.00 0.00 0.00
20) Disable Tracker Proc? TRUE TRUE FALSE ← MODIFIED
21) Tracker Proc Run Mode 0 0 0
22) Conical Alpha Az 200 200 200
23) Conical Alpha El 200 200 200
24) Conical Radius 1.00 1.00 1.00
25) Conical Count Max 20 20 20
26) Conical Test Drift +0 +0 +0
27) Circle RPM 120 120 120
28) Circle Pts/Rev 6 6 6
32) Conical Az Clamp 8.00 8.00 8.00
33) Conical El Clamp 8.00 8.00 8.00
35) Motor Pts/Rev 72 72 72
36) Circle Az Radius 1.00 1.00 1.00
37) Circle El Radius 1.00 1.00 1.00
38) Sleep Mode Timer Secs 420 420 420
40) Motor Type 0 0 0
41) Satellite Scan Velocity 55.00 55.00 55.00
48) Motor Spiral Velocity 55.00 55.00 55.00
49) Motor Gear Ratio 0x00000000 0x00000000 0x00000000
63) GPS Heading Threshold 1.00 1.00 1.00
64) GPS Moving Threshold 5.00 MPH 5.00 MPH 5.00 MPH
66) Spiral Signal In A Row Min +3 +3 +3
67) Spiral Signal In A Row Max +20 +20 +20
68) Signal Odd to Even Offset +0 +0 +0
69) Signal Offset 80 80 80
70) Signal Baseline Angle 65.00 65.00 65.00
71) Signal Re-Peak Degrade Percent 25 25 25
72) Gyro Sensitivity +1110 +1110 +1110
73) Gyro Filter Size +1 +1 +1
74) Gyro Calib Readings 100 100 100
75) Gyro Mount Type 1 1 1
76) Gyro Velocity Offset 4 4 4
77) Gyro Max Accel 600 600 600
80) AZ Max Vel 65.00 65.00 65.00
81) AZ Max Accel 400.00 400.00 400.00
82) AZ Home Velocity 55.00 55.00 55.00
83) AZ Steps/Rev 40000 40000 40000
84) AZ Direction +1 +1 +1
85) EL Max Vel 45.00 45.00 45.00
86) EL Max Accel 400.00 400.00 400.00
87) EL Home Velocity 45.00 45.00 45.00
88) EL Steps/Rev 24960 24960 24960
89) EL Direction +1 +1 +1
95) AZ Low current limit 0x0000ff0c 0x0000ff0c 0x0000ff0c
96) AZ High current limit 0x0000ff30 0x0000ff30 0x0000ff30
97) EL Low current limit 0x0000ff0c 0x0000ff0c 0x0000ff0c
98) EL High current limit 0x0000ff40 0x0000ff40 0x0000ff40
101) Minimum Elevation Angle 18.00 18.00 18.00
102) Maximum Elevation Angle 65.00 65.00 65.00
103) Elevation Home Angle 65.00 65.00 65.00
106) Az Stall Detect 78 78 78
107) El Stall Detect 75 75 75
108) Az Stall Samples 100 100 100
109) El Stall Samples 100 100 100
110) EL Home Current Limit 0x0000ff28 0x0000ff28 0x0000ff28
111) AZ Home Current Limit 0x0000ff40 0x0000ff40 0x0000ff40
112) Disable Dipswitch? FALSE FALSE FALSE
113) Dipswitch Value 101 101 101
114) Dipswitch Front/Rear Mount 0 0 0
115) Mount Offset Angle +0 +0 +0
118) Signal Use LNB Clamp FALSE FALSE FALSE
128) AZ PID Kp +600 +600 +600
129) AZ PID Kv +60 +60 +60
130) AZ PID Ki +1 +1 +1
131) EL PID Kp +250 +250 +250
132) EL PID Kv +50 +50 +50
133) EL PID Ki +1 +1 +1
136) AZ PWM Stall Cnt 6 6 6
137) EL PWM Stall Cnt 5 5 5
143) Tracking Number 0 0 0
Key Parameters for Satellite Tracking
| NVS | Name | Value | Notes |
|---|---|---|---|
| 20 | Disable Tracker Proc? | TRUE | Prevents TV satellite search on boot |
| 83 | AZ Steps/Rev | 40000 | Centidegrees per revolution (400.00°) |
| 88 | EL Steps/Rev | 24960 | ~249.60° per revolution |
| 80 | AZ Max Vel | 65.00 | °/s azimuth max velocity |
| 85 | EL Max Vel | 45.00 | °/s elevation max velocity |
| 101 | Min Elevation | 18.00 | Firmware floor (degrees) |
| 102 | Max Elevation | 65.00 | Firmware ceiling (degrees) |
| 103 | EL Home Angle | 65.00 | Where EL homes to on startup |
| 128-133 | PID Gains | varies | AZ/EL motor PID tuning parameters |
Boot Sequence Observed
Bootloader Phase (<50ms, non-interactive)
Captured via scripts/boot_capture.py with high-resolution timestamps:
[0.050s] 01 00 ← binary status bytes (bootloader→app handshake?)
[0.050s] Bootloader version: 1.01
[0.050s] Application is running...
[0.050s] 98 80 96 ← binary bytes (integrity check? jump address?)
[0.100s] Application Starting Kinetis PCB... power up/reset
The bootloader runs at 115200 baud (same as application — confirmed by multi-baud capture at 9600/19200/38400/57600/230400/460800). There is no interactive window — ESC, CR, BREAK, 0x55 autobaud, and other interrupt sequences at 5-30ms delays all failed to stop the boot. The bootloader checks a flag (likely in EEPROM or a reserved flash sector) and immediately jumps to the application at 0x10000 if no firmware update is pending.
Application Phase (~10s to prompt)
Version 02.02.48
Copyright 2013 - Winegard Company
Boot Complete
Loc Startup: IDU NOT Present
app_dipswitch:101
Primary Update: 10100
Alternate Update: 11900
Toggle Ability Update: 0
Alternate2 Update: 0
Sat Provider Update: 1
DVB: id:0000, lon:101.00E
Tuner = WIDE
Signal offset = 80
Signal baseline angle = 6500
Signal Re-Peak Pct = 25
NVS Status: 0 Sleep: 420 Dipswitch: 101
Sleep: 420 NVS: 420
NoGpsStartUp: 721
STATIONARY MODE
Enabled LNB ODU 18V
GPS Not Found
Homing Sequence
After boot, the dish homes both motors (EL first, then AZ) using stall detection:
MotorHome:1 timeout:2000 ← EL motor homing
Home TwelInch El Velocity: 4500
EL Stall Timeout
El Home Angle: 6500
MotorHome:0 timeout:8000 ← AZ motor homing
Home TwelInch Az
End MotorAzStall:part1
Antenna Facing Front
home:0 wrap_pos:0 wrap_min:-42333 wrap_max:2333
Cable Wrap Limits
From homing output: wrap_min:-42333 wrap_max:2333
- In centidegrees: -423.33° to +23.33° from home position
- Total range: 446.66° (~1.24 full rotations)
Motor Control
Position Query
In the MOT> submenu, a returns position with 4-space indentation:
a
Angle[0] = 180.00 ← AZ (degrees)
Angle[1] = 45.00 ← EL (degrees)
MOT>
Move Command
a <id> <deg> returns a confirmation (no array index) and the prompt immediately
while the motor moves in the background:
a 1 46
Angle = 46.00
MOT>
Observed Motor Behavior
| Test | Command | Target | Actual | Overshoot |
|---|---|---|---|---|
| EL move out | a 1 46 |
46.00 | 46.05 | +0.05° |
| AZ move out | a 0 181 |
181.00 | 181.01 | +0.01° |
| EL return | a 1 45 |
45.00 | 44.94 | -0.06° |
| AZ return | a 0 180 |
180.00 | 179.98 | -0.02° |
Direction-dependent overshoot: the motor consistently overshoots in the direction of travel, undershooting on return. This is classic stepper backlash + PID settling behavior and is what the leapfrog algorithm compensates for.
MOT Submenu — Full Command Reference
Available commands:
a Go to angle [[[motor] [[+|-]angle]]]
azscan Scan AZ from EL Min-Max Angle [az_rel_angle] [el_rel_angle] [delay]
azscanwxp Scan AZ from EL Min-Max with all transponders [motor] [span] [resolution] [num_xp]
e Engage motors
ela2s Elevation Law Test - Angle to Steps [angle]
elminmaxhome Display Min, Max & Home Elevation Angles
els2a Elevation Law Test - Steps to Angle [steps]
h Home Motors [motor num or * (both)]
l List Motors in System
life Az/El Life test [az_rel_angle] [el_rel_angle]
ma Set Max Acceleration [[motor] [deg/sec/sec]]
motorboth Both Motor Life test [AZ delta(0-25)] [EL delta(0-25)]
motorlife Motor Life Test [motor_id] [min_angle] [max_angle]
mv Set Max Velocity [motor] [deg/sec]
p Go To Position [motor] [pos]
pid Set PID Parameters [motor] [Kp] [Kv] [Ki]
r Release Motors
sd Stall Detect [motor] [dir] [timeout_ms] [iterations (0=forever)]
sp Set Position [motor] [pos]
sw Set Wrap Position [motor] [pos]
v Goto Velocity [motor] [deg/rev]
vms Goto Velocity For Milliseconds [motor] [deg/rev] [ms]
w Wrap Manager [motor] [ON/OFF]
Relative Moves
a supports +/- prefix for relative moves:
a 0 +5 ← move AZ 5° CW from current
a 1 -2 ← move EL 2° down from current
This is undocumented in the upstream repos and very useful for incremental positioning during tracking.
Motor List
l
Motors:
0 - AZIMUTH: local
1 - ELEVATION: local
"local" means direct A3981 driver control (vs. a networked motor controller).
Motor Dynamics
ma → Accel[0] = 400.0 Accel[1] = 400.0 (deg/sec²)
mv → Max Vel[0] = 65.0 Max Vel[1] = 45.0 (deg/sec)
Both axes have identical acceleration (400 deg/sec²). AZ max velocity is faster (65 deg/sec) than EL (45 deg/sec) — different gear ratios and mechanical loads.
Step Position
p shows position in microsteps:
p → Position[0] = 19998 Position[1] = 3116
Cross-check with angle: AZ 179.98° × (40000/360) = 19998 steps. Linear mapping for both axes (angle = steps × 360 / steps_per_rev).
Elevation Limits
elminmaxhome → Min: 1800 Max: 6500 Home: 6500
All values in centidegrees: Min=18.00°, Max=65.00°, Home=65.00°. The home position is at maximum elevation (stow position).
Elevation Law Conversion
ela2s converts angle → steps, els2a converts steps → angle:
| Angle | Steps | Notes |
|---|---|---|
| 0° | 1248 | Below min (warning: "Min: 1800") |
| 18° | 1248 | Minimum EL (same steps as 0°) |
| 45° | 3120 | |
| 65° | 4506 | Maximum EL |
| 90° | 4506 | Above max (warning: "Max: 6500") |
The mapping is linear: steps ≈ angle × (24960/360). The "law" is a simple linear function for this dish — no non-linear linkage compensation.
Engage / Release
e— Engage motors (enable A3981 drivers, PID loop holds position)r— Release motors (disable drivers, dish can move freely by hand)
Sky Scan Commands
azscan <az_rel> <el_rel> <delay> — Scan AZ while stepping EL from
min to max angle. Parameters are relative angles and dwell delay. Used for
basic signal surveys.
azscanwxp <motor> <span> <resolution> <num_xp> — Advanced sky scan
that steps in hundredths of a degree and checks all transponders at each
position. This is the core of Davidson's winegard-sky-scan project. The
resolution parameter in hundredths of a degree enables 0.01° precision
scanning — far finer than the standard a command.
Stall Detection
sd <motor> <dir> <timeout> [iterations] — Run stall detection on a
motor. Default timeouts: AZ=10000ms, EL=2000ms. The firmware drives the
motor in the specified direction until it stalls (current spike from A3981).
Set iterations to 0 for continuous mode. Used during homing and calibration.
Life / Durability Tests
Factory test commands that continuously exercise the motors:
life <az_rel> <el_rel>— Oscillate both axes by relative anglesmotorlife <id> <min> <max>— Sweep a single motor between min/max anglesmotorboth <az_delta> <el_delta>— Exercise both motors, max 25° delta each
Write-Only Commands
These commands require parameters — no read-only mode:
pid <motor> <Kp> <Kv> <Ki>— Set PID gains (no read command)sp <motor> <pos>— Set step position counter (doesn't move motor)sw <motor> <pos>— Set wrap positionw <motor> ON/OFF— Enable/disable wrap manager
DVB Subsystem (BCM4515)
Hardware
BCM Hardware= ID: 0x4515 VER: 0xB0
BCM Firmware= MAJOR VER: 0x71 (113) MINOR VER: 0x25 (37)
BCM Strap Config: 0x25018
Channel Parameters (dis)
Power Mode: ON
Search Transponders: ON
Auto Search Mode: 1
Shuffle Mode: ON
Frequency List: Non-Stacked
Num Parameter Current Default
1 Frequency 1090640 (kHz) 974000 (kHz)
2 Symbol Rate 0 (PeakScanEnabled) 20000 (ksps)
3 Trans_Mod_CRate blind_scan blind_scan
4 Blind Scan Mode ___trb_dvb_dss_____ ___trb_dvb_dss_____
5 LNB Polarity ODU:13V ---
6 LNB Tone (ODU) off off
7 Roll-off 0.35 0.35
8 LPF Cutoff 0 (auto) 0 (MHz)
9 Carrier Offset 0 (kHz) 0 (kHz)
10 FreqSearchRange 5000 (kHz) 5000 (kHz)
11 DCII Mode dcii_qpsk_comb dcii_qpsk_comb
12 Spectral Inv scan scan
13 PScnSymRtRngMin 18000 (ksps) 18000 (ksps)
14 PScnSymRtRngMax 24000 (ksps) 24000 (ksps)
15 SignalDetectMode off off
RSSI Response Format
rssi 5
iterations:5 interval(msec):20
Reads:5 RSSI[avg: 500 cur: 500]
DVB>
500 is the noise floor (no signal lock, dish pointed at arbitrary sky).
LNB Voltage
lnbdc odu enables LNA at 13V. lnbv streams continuous voltage readings:
Reads:1 LNB Voltage (mV): 13239 ( ADC value: 119 )
Reads:2 LNB Voltage (mV): 13182 ( ADC value: 118 )
...
Stable at ~13.11V (ADC 117). Boot default is 18V; lnbdc odu switches to 13V.
13V = vertical polarization, 18V = horizontal polarization on a standard LNB.
AGC (Streaming)
agc streams RF and IF automatic gain control plus SNR/NID:
Reads:1 RF_AGC[avg: 1327353088 cur: 1327353088] IF_AGC[avg: 2684354560 cur: 2684354560] SNR: 0.0 NID: FFFF/none
- RF_AGC values are raw BCM4515 32-bit register values
- IF_AGC constant at 0xA0000000 (fixed IF gain)
- SNR: 0.0 when no signal lock
- NID: FFFF/none = no DVB network ID detected
SNR (Streaming)
snr streams signal-to-noise ratio readings:
Reads:1 SNR[avg: 0.0 cur: 0.0]
Reads:2 SNR[avg: 0.0 cur: 0.0]
...
Reads:223 SNR[avg: 0.0 cur: 3.1] ← transient RF spike
Reads:250 SNR[avg: 0.0 cur: 2.6] ← another transient
At noise floor, average stays 0.0 but occasional transient spikes appear in
the cur field — fleeting RF energy. Stays in DVB submenu when interrupted.
Lock Status (ls) — Streaming
ls is a STREAMING command that continuously scans all 32 transponders, trying
multiple modulations per frequency. Output is a continuous scan log:
Xp:1 Freq:974000 SymRate:20000 Mode:blind_scan ... no_lock
Xp:1 Freq:974000 SymRate:20000 Mode:turbo_qpsk_... ... no_lock
...
When interrupted with CR, ls prints "Terminating shell." and drops to
TRK> (exits DVB submenu entirely). This is unique among DVB streaming
commands — all others stay in DVB submenu when interrupted.
Quick Lock Status (qls) — Streaming
qls streams compact lock status at ~100ms intervals:
Lock:0 rssi:500 cnt:0
Lock:0 rssi:500 cnt:0
...
Stays in DVB submenu when interrupted. Ideal for real-time signal monitoring during dish movement (low bandwidth, high update rate).
Network ID (nid) — Streaming
nid streams network identification:
nid: FFFF/none
nid: FFFF/none
Uses CR-overwrite (carriage return without newline) for in-place updates. FFFF = no DVB network detected. Can be difficult to interrupt cleanly — may need multiple CR + flush cycles.
Signal Statistics (stats) — Streaming
stats streams signal statistics. Produces no output when there is no signal
lock. Stays in DVB submenu when interrupted.
Diagnostics (diag) — One-Shot
diag is a multi-block ONE-SHOT command that outputs detailed per-transponder
diagnostics for each transponder currently being tried:
SymRate: 18514984 Freq: 974000
Bit Rate: 29893160
SNR: 0.0 SymRateErr: -1258 CarrierOffset: 0 CarrierErr: 3295097
Tuner LPF: 12 RF_AGC: 2214707264
BER Errors: 0 MPEG Frm Errors: 0 MPEG Frm Count: 0
Reacquisitions: 7
RS Corr/Uncorr: 0 / 0 Pre-Vit: 0
sp inv: scan phase rotation: 0
Acq Time: 4259 msec
trans_mod_coderate: no_lock
Tuner PLL: LOCKED Internal BERT: not locked
Demod: not locked Timing Loop Lock: disabled
Transponder Table (table) — One-Shot (Long)
table generates a full scan across all 32 transponders. Takes ~136 seconds
(~4.25s per transponder, matching tabto acquisition timeout of 4000ms).
The output starts with a configuration summary showing all modifiable parameters with their current values and the commands to change them, then a detailed per-transponder table with columns:
Xp Freq SigDet SymRate PeakPower SNR LPF RF_AGC AcqTime Mode
NID/SAT XpTime LNBVsens RSSI BitrateOut SymRateError CarrierOffset
CarrierError BER_Errors MPEG_Errors MPEG_Count Reacq
With shuffle mode ON, transponders scan in interleaved groups of 4: 1,2,3,4 → 17,18,19,20 → 5,6,7,8 → 21,22,23,24 → 9,10,11,12 → 25,26,27,28 → 13,14,15,16 → 29,30,31,32.
LNB voltage alternates between ~13V (vertical polarization) and ~20V (horizontal polarization) across transponder groups.
After scan completes: Table Completion Time (seconds): 136 Transponders Locked: 0
then auto-restores LNB to STB mode: Enabled LNB STB.
Frequency List (freqs)
Returns the active frequency list name:
freqs → Non-Stacked
"Non-Stacked" means standard Ku-band IF frequencies without stacking (stacked LNBs combine multiple bands into one cable).
Transponder Range (range)
range → Transponder Range: [ 1 - 32 ]
Power State (pwr)
pwr → SDS and DiSEqC core power enabled
Shows power state of the Satellite Detection System and DiSEqC subsystems.
Search Mode (srch_mode)
srch_mode → Auto Search Mode: 1
Mode 1 = automatic transponder search with blind scan.
Timeout Settings
Two separate timeout configurations:
Table scan timeouts (tabto):
Timeout (msec): Acq:4000 NID:20000 Signal Detect:0
Single transponder tune timeouts (to):
Timeout (msec): Acq:500 NID:12000
tabto takes arguments: tabto <acq> <nid> <sd> (set 0 to disable NID/SD).
to takes arguments: to <acq> <nid>.
Toggle Commands
These commands toggle a mode and print the new state:
| Command | What it toggles |
|---|---|
srch |
Search Transponders mode (on/off) |
shuf |
Transponder shuffle order (on/off) |
tablex |
Extended table mode (on/off) |
Defaults Reset (def)
def silently resets all channel parameters to defaults. No output, no
confirmation. Use with caution — there is no undo.
Modulation Switch (msw)
msw requires arguments but rejects all tested inputs (msw 0, msw 1,
msw on). Format unknown — possibly vestigial or requires a specific
modulation string.
Channel Parameter Help (h <n>)
h <n> shows valid values for each of the 13+2 channel parameters:
| Param | Name | Range / Values |
|---|---|---|
| 1 | Frequency | 250000–2150000 kHz (L-band IF) |
| 2 | Symbol Rate | 0 (Peak Scan) or 2000–45000 ksps |
| 3 | Trans_Mod_CRate | 30+ modes (see table below) |
| 4 | Blind Scan Mode | Bitmask: s2, trb, dvb, dss, dcii (see below) |
| 5 | LNB Polarity | 13V (vertical) / 18V (horizontal) |
| 6 | LNB Tone | off / on (22kHz for high-band switching) |
| 7 | Roll-off | 0.20 / 0.35 |
| 8 | LPF Cutoff | 0 (auto) or 1–40 MHz |
| 9 | Carrier Offset | -5000 to +5000 Hz |
| 10 | FreqSearchRange | 0–10000 kHz |
| 11 | DCII Mode | comb, i, q (QPSK) / ocomb, oi, oq (OQPSK) |
| 12 | Spectral Inversion | 0=normal, 1=q inv, 2=i inv, 3=scan |
| 13 | PScnSymRtRngMin | 2000–45000 ksps |
| 14 | PScnSymRtRngMax | (shown in dis, no h 14 help) |
| 15 | SignalDetectMode | (shown in dis, no h 15 help) |
Supported Modulations (Param 3)
DVB-S: dvbs_qpsk_1_2, dvbs_qpsk_2_3, dvbs_qpsk_3_4,
dvbs_qpsk_5_6, dvbs_qpsk_7_8
DSS: dss_qpsk_1_2, dss_qpsk_2_3, dss_qpsk_6_7
DCII: dcii_qpsk_1_2, dcii_qpsk_2_3, dcii_qpsk_3_4,
dcii_qpsk_5_11, dcii_qpsk_4_5
DVB-S2: s2_qpsk_1_2, s2_qpsk_3_5, s2_qpsk_2_3, s2_qpsk_3_4,
s2_qpsk_4_5, s2_qpsk_5_6, s2_qpsk_8_9, s2_qpsk_9_10,
s2_8psk_3_5, s2_8psk_2_3, s2_8psk_3_4
Turbo: turbo_qpsk_1_2, turbo_qpsk_2_3, turbo_qpsk_3_4,
turbo_qpsk_5_6, turbo_qpsk_7_8,
turbo_8psk_2_3, turbo_8psk_3_4, turbo_8psk_4_5,
turbo_8psk_5_6, turbo_8psk_8_9
Special: blind_scan
Blind Scan Mode Bitmask (Param 4)
Visual bitmask format: s2_trb_dvb_dss_dcii
| Value | Pattern | Standards enabled |
|---|---|---|
s2 |
s2_______________ |
DVB-S2 only |
trb |
___trb___________ |
Turbo FEC only |
dvb |
_______dvb_______ |
DVB-S only |
dss |
___________dss___ |
DSS (DirecTV) only |
dcii |
_______________dcii |
DCII (DigiCipher) only |
all |
s2_trb_dvb_dss_dcii |
All standards |
dish |
___trb_dvb________ |
DISH Network (turbo + DVB-S) |
3 |
___trb_dvb_dss___ |
DirecTV 3-standard |
shaw |
___trb_________dcii |
Shaw (turbo + DCII) |
nodcii |
s2_trb_dvb_dss___ |
All except DCII |
nos2 |
___trb_dvb_dss_dcii |
All except DVB-S2 |
DiSEqC Commands
All DiSEqC 2.x read commands fail with RxReplyTimeout when no switch is
connected (expected — the G2 has a direct LNB without a multi-switch):
| Command | Function | Result (no switch) |
|---|---|---|
di2id |
Read LNB hardware ID | LNB Read HW ID FAIL |
di2stat |
Read LNB status | LNB Read Status FAIL |
di2conf |
Read LNB config | LNB Read Config FAIL |
di2sc |
Short circuit test | LNB Short Circuit FAIL |
di2rcs |
Read switch state | LNB Switch State FAIL |
di2cs |
Configure switch | No Parameters Specified (needs args) |
send <3-6 bytes> |
Raw DiSEqC packet | (not tested — no switch) |
DiSEqC Timing Parameters
| Command | Parameter | Default |
|---|---|---|
ovraddr |
LNB address | 0x11 (standard first LNB) |
rrto |
Receive reply timeout | 210 ms |
tdthresh |
Tone detect threshold | 110 (units: 0.16 counts/mV) |
pretx |
Pre-command TX delay | 15 ms |
DVB Command Reference
| Command | Type | Description |
|---|---|---|
rssi <n> |
One-shot | Average signal strength over n samples |
snr |
Streaming | SNR readings (avg + current) |
agc |
Streaming | RF/IF AGC + SNR + NID |
lnbdc odu |
One-shot | Enable LNB in ODU mode (13V) |
lnbv |
Streaming | Continuous LNB voltage monitoring |
ls |
Streaming | Full transponder lock scan (exits DVB on interrupt!) |
qls |
Streaming | Quick lock status (~100ms updates) |
nid |
Streaming | Network ID (CR-overwrite display) |
stats |
Streaming | Signal statistics (silent when no lock) |
config |
One-shot | BCM hardware/firmware version |
dis |
One-shot | Display all channel parameters |
diag |
One-shot | Multi-block per-transponder diagnostics |
table |
One-shot | Full 32-transponder scan (~136s) |
freqs |
One-shot | Frequency list name |
range |
One-shot | Transponder scan range |
pwr |
One-shot | SDS/DiSEqC power state |
srch_mode |
One-shot | Auto search mode value |
tabto |
Read/Write | Table scan timeouts (acq/nid/sd) |
to |
Read/Write | Single tune timeouts (acq/nid) |
t <n> |
Write | Select transponder |
e <n> <v> |
Write | Edit channel parameter |
h <n> |
Read | Parameter help (valid values for param n, 1-13) |
srch |
Toggle | Search transponders mode |
shuf |
Toggle | Transponder shuffle order |
tablex |
Toggle | Extended table mode |
def |
Write | Reset all params to defaults (silent, no undo!) |
msw <?> |
Write | Modulation switch (format unknown) |
ovraddr [addr] |
Read/Write | DiSEqC LNB address |
rrto [ms] |
Read/Write | DiSEqC receive reply timeout |
tdthresh [val] |
Read/Write | DiSEqC tone detect threshold |
pretx [ms] |
Read/Write | DiSEqC pre-TX delay |
di2id |
Read | DiSEqC 2.x: read LNB hardware ID |
di2stat |
Read | DiSEqC 2.x: read LNB status |
di2conf |
Read | DiSEqC 2.x: read LNB config |
di2sc |
Read | DiSEqC 2.x: short circuit test |
di2rcs |
Read | DiSEqC 2.x: read switch state |
di2cs <args> |
Write | DiSEqC 2.x: configure switch |
send <3-6 hex> |
Write | Raw DiSEqC packet |
Streaming commands: snr, agc, lnbv, qls, nid, stats run until
CR interrupts. All stay in DVB submenu except ls which drops to TRK>.
Toggle commands: srch, shuf, tablex alternate on/off and print new state.
Satellite Configuration
DVB: id:0000, lon:101.00E ← DirecTV 101°W (stored as East longitude)
Primary Update: 10100 ← 101.00° in centidegrees
Alternate Update: 11900 ← 119.00°
Sat Provider Update: 1 ← Provider ID
Dipswitch Value: 101 ← DirecTV configuration
A3981 Motor Driver IC
The Allegro A3981 is an automotive-grade programmable stepper motor driver controlled by the K60 MCU via SPI. Two A3981 chips — one per axis (AZ, EL).
Datasheet: docs/A3981-datasheet.pdf (Allegro Microsystems)
ECAD files: docs/A3981-ecad.kicad_sym, docs/A3981-ecad.pretty/
A3981 Commands (A3981>)
| Command | Type | Description |
|---|---|---|
diag |
Read | Fault diagnostics for both axes |
sm |
Read | Step size mode (AUTO/MANUAL) |
ss |
Read | Current step size (microstepping level) |
cm |
Read | Current control mode (AUTO/MANUAL) |
st <params> |
Write | Set torque/current parameters |
reset |
Write | Clear latched A3981 faults on both axes |
Diagnostics
diag
AZ DIAG: OK
EL DIAG: OK
Microstepping Configuration
ss
KEY: FULL-16, HALF-8, QTR-4, EIGHTH-2, SIXTEENTH-1
AZ Step Size:1
EL Step Size:1
Both axes at step size 1 = 1/16 microstepping (finest available). The A3981 supports full, half, quarter, eighth, and sixteenth steps. The inverted key (FULL=16, SIXTEENTH=1) is the firmware's internal representation — likely a divisor applied to the full-step pulse count.
Step Size and Current Modes
sm cm
AZ Step Size Mode = AUTO AZ: Mode = AUTO
EL Step Size Mode = AUTO EL: Mode = AUTO
AUTO mode means the driver dynamically adjusts microstepping resolution and current level based on motor speed. At low speeds, fine microstepping (1/16) provides smooth motion and precise positioning. At higher speeds, the driver may switch to coarser steps to maintain torque.
Fault Reset
reset
Az/El A3981 Faults Reset.
Clears latched fault conditions (overcurrent, open load, thermal). This is a
write operation — it actively clears the fault registers, not a read-only
status check. Use diag for non-destructive fault checking.
ADC Subsystem (ADC>)
| Command | Type | Description |
|---|---|---|
m |
Streaming | Monitor RSSI continuously |
rssi |
One-shot | Read RSSI (same noise-floor value as DVB) |
scan |
Unknown | Scan ADC on azimuth axis |
bdid |
One-shot | Board identification string |
bdrevid |
One-shot | Board revision ID |
Board Identification
bdid → STATIONARY
bdrevid → A
"STATIONARY" confirms this is the non-mobile (non-in-motion) variant. Board revision "A" is the first production revision.
RSSI via ADC
rssi → 500
Same noise floor value (500) as the DVB rssi command. The ADC subsystem reads
the same analog signal path — likely a baseband power detector output from the
BCM4515 routed to a K60 ADC input.
Dipswitch (DIPSWITCH>)
dipswitch
val:ffffff01
app_dipswitch:101
The raw value 0xFFFFFF01 has the low byte = 0x01. The app_dipswitch value
101 corresponds to the DirecTV satellite configuration. Dipswitch values select
the satellite provider (DirecTV, DISH, Bell) and determine which transponder
frequencies the search algorithm tries.
EEPROM (EE>)
| Command | Type | Description |
|---|---|---|
ee <idx> |
Read | Read EEPROM value at index |
ee <idx> <val> |
Write | Write EEPROM value at index |
inv [<idx>] |
Write | Invalidate EEPROM index (destructive!) |
def |
Write | Restore all EEPROM values to factory defaults |
EEPROM is a separate storage area from NVS. NVS holds operational parameters (motor tuning, PID gains, satellite config). EEPROM appears to hold calibration or factory data — likely stored in an external I2C EEPROM (AT24Cxx or similar) rather than the K60's internal flash.
EEPROM Structure
The EEPROM has exactly 17 indices (0-16). The firmware enforces bounds:
ee 17
Index out of bounds:17 Min:0 Max:16
Each index stores a 32-bit unsigned integer. Invalid entries return a sentinel value of 65793 (0x00010101) — three bytes of 0x01 in the low 24 bits. The firmware distinguishes between valid and invalid reads:
- Valid:
Read value = <decimal>— data is trusted - Invalid:
Failed to read. val:<decimal>— data exists but flagged invalid
Complete EEPROM Dump
Dumped via scripts/ee_dump.py (pyserial script scanning all indices):
| Index | Decimal | Hex | Status | Notes |
|---|---|---|---|---|
| 0 | 65793 | 0x00010101 | INVALID | Accidentally invalidated (inv 0) |
| 1 | 0 | 0x00000000 | OK | |
| 2 | 22897 | 0x00005971 | OK | |
| 3 | 3748 | 0x00000EA4 | OK | |
| 4 | 4346 | 0x000010FA | OK | |
| 5 | 11637 | 0x00002D75 | OK | |
| 6 | 65793 | 0x00010101 | INVALID | Factory default (never written) |
| 7 | 65793 | 0x00010101 | INVALID | Factory default |
| 8 | 65793 | 0x00010101 | INVALID | Factory default |
| 9 | 65793 | 0x00010101 | INVALID | Factory default |
| 10 | 65793 | 0x00010101 | INVALID | Factory default |
| 11 | 65793 | 0x00010101 | INVALID | Factory default |
| 12 | 65793 | 0x00010101 | INVALID | Factory default |
| 13 | 65793 | 0x00010101 | INVALID | Factory default |
| 14 | 65793 | 0x00010101 | INVALID | Factory default |
| 15 | 65793 | 0x00010101 | INVALID | Factory default |
| 16 | 65793 | 0x00010101 | INVALID | Factory default |
Only 6 indices (0-5) were ever written with valid data. Indices 6-16 have never been programmed — all contain the 0x00010101 sentinel. The EEPROM has capacity for 17 values but only the first 6 are used by this firmware version.
Value Analysis
The valid EEPROM values (indices 1-5) don't correspond to any obvious motor positions, NVS indices, or physical constants. They may represent:
- Factory calibration offsets — motor encoder corrections, sensor trim
- Manufacturing data — serial number components, PCB revision, test results
- Checksum/signature — authentication for firmware/hardware pairing
Index 0 (originally valid, now invalidated) was likely a calibration value or
header byte. Its original value is unknown — def might restore it.
Command Notes
inv [<idx>] — "Invalidate", not "inventory". Marks an index as invalid
by writing the 0x00010101 sentinel. The firmware still returns the raw value
on read but flags it as Failed to read. Destructive and immediate — no
confirmation prompt.
def — Restores all EEPROM indices to factory defaults. Not tested on this
unit (would overwrite the accidentally-invalidated index 0 but also reset any
user-modified values). Worth trying if index 0's missing value causes issues.
ee <idx> <val> — Write a value to an index. Accepts decimal integers.
Can be used to restore invalidated indices if the original value is known.
GPIO Registers (GPIO>)
| Command | Type | Description |
|---|---|---|
regs |
Read | Dump all GPIO pin states (0/1) |
r <port> <pin> |
Read | Read single GPIO pin (e.g., r A 5) |
w <port> <pin> |
Write | Write to GPIO pin (toggle) |
dir <bit> <val> |
Write | Set pin direction (input/output) |
The regs command dumps all MCU GPIO pins across 5 ports (K60 144-pin).
Note: pins A20-A23 and B12-B15 are not enumerated (reserved or unbonded):
| Port | Pins Enumerated | High Pins (=1) |
|---|---|---|
| A | A0–A19, A24–A29 (26 pins) | A1, A3, A4, A5, A15, A16, A25–A29 |
| B | B0–B11, B16–B23 (20 pins) | B0, B1, B2, B3, B11 |
| C | C0–C19 (20 pins) | C10, C11, C12, C13, C18 |
| D | D0–D15 (16 pins) | D11, D12, D13 |
| E | E0–E12, E24–E29 (19 pins) | E0, E1, E2, E4, E5, E7, E9–E12, E24–E28 |
Total: 101 pins enumerated. "Unknown bit E29" logged by firmware — pin E29 is defined in hardware but not assigned a function (test point or reserved).
LATLON (LATLON>)
| Command | Type | Description |
|---|---|---|
l <satlon1> <satlon2> <satel1> <satel2> |
Read | Calculate dish lat/lon from two satellite observations |
This is the dish's self-localization algorithm — it triangulates its own geographic position by observing two known geostationary satellites. The four parameters are longitude and elevation pairs for two satellites.
l -110 -119 40 38
anglesentered = -11000 -11900 4000 3800
Lat = 4295 Lon = 25655
Output is in centidegrees: Lat 4295 = 42.95°N, Lon 25655 = 256.55°E (= 103.45°W). This is a reasonable US mid-latitude position given DISH Network satellites at 110°W and 119°W with elevation angles of 40° and 38°.
The firmware uses this for automatic satellite look-angle computation when no GPS is available (the G2 has no GPS module — "GPS Not Found" at boot).
PEAK Subsystem (PEAK>)
| Command | Type | Description |
|---|---|---|
ts |
Streaming (DANGEROUS) | EchoStar/DiSEqC switch toggle — runs forever, can't be interrupted |
pw <az> <el> |
Motor+Signal | Peak wide — sweep around AZ/EL to find signal peak |
psnr <az> <el> |
Motor+Signal | Peak SNR — sweep around AZ/EL optimizing for SNR |
pxy1 [<n>] |
Motor+Signal | Peak XY1 — 2D cross-pattern peak search, repeat n times (default 1) |
stb |
One-shot | STB control test — toggles LNB polarity and compares RSSI |
rssits |
Streaming | RSSI test — prints every 1 minute (exits submenu on interrupt!) |
EchoStar Switch Toggle (ts) — DANGEROUS!
The ts command runs indefinitely, probing for a DiSEqC/EchoStar switch
by toggling LNB voltage and reading the switch response. It cannot be stopped
by sending q — the running command consumes all input. The only escape is to
close and reopen the serial port.
ts
(14000+ reads, all showing: 0b0000 0)
All reads returned 0b0000 0 — no switch connected (expected, since the G2's
LNB is directly connected without a multi-switch).
STB Control Test (stb)
Toggles LNB polarization and compares RSSI at each polarity:
stb
Enabled LNB ODU 18V
Even_sig = 504
Enabled LNB STB
Odd_sig = 232
Enabled LNB STB
Odd_sig = 233 Ctr = 0
Enabled LNB ODU 18V→ horizontal polarization (18V),Even_sig = 504Enabled LNB STB→ vertical polarization (13V),Odd_sig = 232Ctr = 0→ no DiSEqC switch response detected- The 504 vs 232 difference (~2x) reflects the polarization-dependent noise floor. "Even" = H-pol transponders (18V), "Odd" = V-pol (13V) — DBS convention.
RSSI Test (rssits)
Starts a background RSSI monitoring task that prints every 1 minute. When
interrupted, exits the PEAK submenu entirely (drops to TRK> root),
similar to DVB ls behavior.
STEP Subsystem (STEP>) — Low-Level Motor Control
| Command | Type | Description |
|---|---|---|
e |
Write | Engage motor (enable driver) |
r |
Write | Release motors (disable driver, coast to stop) |
p |
Read/Write | Go to position (absolute, in microsteps), or read current position |
v |
Write | Go to velocity (continuous rotation) |
ma |
Read/Write | Set/get max acceleration (ustep/sec/msec) |
mv |
Read/Write | Set/get max velocity (ustep/sec) |
pid |
Read/Write | Set/get PID tuning values |
This is the raw stepper motor interface — below the mot menu's degree-based
abstraction. Commands operate in microsteps rather than degrees. The
relationship between microsteps and degrees depends on:
- Gear ratio (NVS 49, currently 0x00000000)
- Steps per revolution (NVS 83: AZ=40000, NVS 88: EL=24960)
- Microstepping level (A3981: both at 1/16)
Current Values (read mode)
p → Step Pos[0] = 19998 Step Pos[1] = 3116
ma → Accel[0] = 44 Accel[1] = 28
mv → Max Vel [0] = 7222 Max Vel [1] = 3120
pid → Kp=250 Kv=50
MOT↔STEP Conversion Table
| Parameter | STEP (microsteps) | MOT (degrees) | Factor |
|---|---|---|---|
| AZ position | 19998 | 179.98° | 111.11 steps/° |
| EL position | 3116 | 44.94° | 69.33 steps/° |
| AZ max vel | 7222 ustep/s | 65.0°/s | ÷111.11 |
| EL max vel | 3120 ustep/s | 45.0°/s | ÷69.33 |
| AZ accel | 44 ustep/s/ms | ~396°/s² | ×1000÷111.11 |
| EL accel | 28 ustep/s/ms | ~404°/s² | ×1000÷69.33 |
PID Tuning
pid returns Kp=250 Kv=50 — proportional and velocity gains. No Ki term,
indicating a PD (proportional-derivative) position loop. This is typical for
stepper motors which don't drift under holding torque. Matches NVS indices
128-129. Note: pid is write-only in MOT> but readable in STEP>.
The e/r commands engage and release the A3981 motor drivers. When released,
the motors are unpowered and the dish can be moved by hand (useful for manual
positioning or emergency stow). When engaged, the PID loop holds position.
ADC Subsystem (ADC>)
| Command | Type | Description |
|---|---|---|
bdid |
Read | Board ID — returns STATIONARY |
bdrevid |
Read | Board revision ID — returns A |
rssi |
Read | Single RSSI reading (ADC value, not DVB RSSI) |
m |
Toggle? | Monitor RSSI — returns immediately, may enable background monitoring |
scan |
Streaming | Continuous ADC scan with position, RSSI, Lock, SNR, and delta |
Board Identity
- Board ID:
STATIONARY— distinguishes from mobile/in-motion antenna variants - Board Rev ID:
A— PCB revision
RSSI (ADC)
rssi returns a single ADC reading: 232 at noise floor. This is the raw
ADC value from an analog RSSI detector (separate from the DVB tuner's digital
RSSI). The ADC RSSI baseline (~232) corresponds to the DVB Odd_sig in the
PEAK stb test, suggesting both measure the same RF path at V-pol.
Scan (Streaming)
scan performs a continuous RF scan at the current position:
Starting position AZ:17998 EL:4494 Stop at:0
Motor:0 Angle:17998 RSSI:232 Lock:0 SNR: 0.0 Scan Delta:0
Motor:0 Angle:17998 RSSI:238 Lock:0 SNR: 0.0 Scan Delta:0
Motor:0 Angle:17998 RSSI:233 Lock:0 SNR: 0.4 Scan Delta:0
...
Positions are in centidegrees (17998 = 179.98°). Scan Delta:0 indicates no
motor movement — the scan reads RSSI at the fixed position. Stop at:0 suggests
it scans until manually interrupted. Occasional SNR transients (0.1–0.4 dB)
appear from fleeting RF energy. The scan likely moves the motor when used with
the azscanwxp sky-scan command from the MOT menu.
Dipswitch Subsystem (DIPSWITCH>)
| Command | Type | Description |
|---|---|---|
dipswitch |
Read | Read physical DIP switch state and interpreted value |
dipswitch
val:ffffff01
app_dipswitch:101
val: ffffff01— raw GPIO register reading. The0x01LSB indicates one switch position is active;0xFFbytes are pulled-up (inactive) switches.app_dipswitch: 101— firmware-interpreted value. Matches NVS index 113 ("Dipswitch Value: 101"), which encodes a DirecTV satellite configuration.
The physical DIP switch on the PCB selects the satellite provider's transponder list for TV search mode. NVS index 112 ("Disable Dipswitch?") controls whether the firmware reads the physical switch or uses the NVS-stored value. Since we've disabled the tracker (NVS 20 = TRUE), the DIP switch setting is ignored.
A3981 Motor Driver (A3981>)
| Command | Type | Description |
|---|---|---|
diag |
Read | Read AZ/EL diagnostic pins (fault status) |
sm |
Read/Write | Get/set step size mode (AUTO, manual) |
ss |
Read/Write | Get/set step size (microstepping divisor) |
st |
Read/Write | Get/set torque level (HIGH/LOW current) |
cm |
Read/Write | Get/set current control mode (AUTO, manual) |
reset |
Write | Reset AZ/EL A3981 diagnostic/fault registers |
Current State
diag → AZ DIAG: OK EL DIAG: OK
sm → AZ: Step Size Mode = AUTO EL: Step Size Mode = AUTO
ss → AZ: Step Size:1 EL: Step Size:1
st → AZ Torq:LOW EL Torq:LOW
cm → AZ: Mode = AUTO EL: Mode = AUTO
Step Size Key
The ss value is a microstepping divisor, not multiplier:
| Value | Mode | Microsteps per full step |
|---|---|---|
| 16 | FULL | 1 (full step) |
| 8 | HALF | 2 |
| 4 | QTR | 4 |
| 2 | EIGHTH | 8 |
| 1 | SIXTEENTH | 16 (finest) |
Both motors are at 1 (1/16 microstepping — finest resolution). In AUTO
mode, the A3981 IC automatically selects the step size based on speed: finer
steps at low speed for smooth positioning, coarser steps at high speed for
torque.
Torque and Current Control
st shows torque is LOW (motors idle/holding). cm shows current control
is AUTO — the firmware switches between high current (moving) and low current
(holding) automatically. This reduces power consumption and heat when the dish
is stationary.
The diag pins expose A3981 fault conditions: overcurrent, overtemperature,
open-load (disconnected motor winding), or short-to-ground. OK means no
faults detected. Use reset to clear latched fault flags.
OS Subsystem (OS>)
| Command | Type | Description |
|---|---|---|
id |
Read | Full MCU and firmware identification |
reboot |
Write | Reboot microcontroller (confirmed — full boot cycle ~10s) |
System Identification (id)
NVS Version: 1.02.13
System ID: TWELINCH
K60-144pin
Silicon Rev 2.4
Mask Set 4N22D
512 kBytes of P-flash
P-flash only
128 kBytes of RAM
Board Rev ID: A
Board ID: STATIONARY
Ant ID: 12-IN G2
Software version: 02.02.48
CCLK: 96000000
BCLK: 48000000
Flash Base Address: 65536
Flash Size: 458752
Key details:
- System ID:
TWELINCH— "Twelve Inch" (12" dish diameter) - Ant ID:
12-IN G2— Carryout G2 model identifier - Silicon: K60-144pin, Rev 2.4, Mask 4N22D — NXP MK60DN512VLQ10
- Clocks: CCLK=96 MHz (core), BCLK=48 MHz (bus)
- Flash: Base at 0x10000 (64KB), size 458752 bytes (448KB usable firmware space)
- NVS Version: 1.02.13 — the non-volatile storage schema version
Note: tasks and kill commands (available on other variants like HAL 0.0.00)
are not present in the G2's OS submenu. On the G2, the satellite search is
disabled permanently via NVS index 20 instead of killing a running task.
Firmware Command Behavior Notes
Command Types
- One-shot: Executes once, returns result, shows prompt (
>). Safe. - Streaming: Runs indefinitely until interrupted. Some accept
qto stop, others require closing the serial port. Thets,agc, andlnbvcommands are known streamers. - Write: Modifies state (motor position, NVS values, fault registers). Use with caution.
Serial Protocol Notes
- Firmware expects ASCII CR (
0x0D) as line terminator - Response terminates with
>prompt character (ASCII 62) - Console does not support backspace — press Enter to clear on typo
- Streaming commands consume all serial input while running
- Some commands (e.g.,
st) are setters that return "Invalid params" when called without arguments — they are not read-only despite appearing in help listings without obvious setter syntax
TODO: Physical Board Inspection
Tasks for when the dome housing is opened and the control PCB is accessible.
USB Port Investigation
- Inspect LQFP-144 package pins 19-22 (USB0_DP, USB0_DM, VOUT33, VREGIN) — these are on the corner near pin 1. Look for traces routing to a connector, test pads, or unpopulated header
- Look for a USB mini/micro/Type-A connector anywhere on the PCB (may be behind a panel, under a shield can, or on the back side)
- Check for a VBUS sense GPIO trace — the K60 needs a GPIO pin to detect USB cable insertion (ref manual Section 3.9.2)
- If USB pads found: probe with multimeter for continuity to K60 pins 19-22
Debug / Programming Interface
- Look for SWD/JTAG debug header (K60 has dedicated SWD pins: SWDIO on PTA3/pin 50, SWDCLK on PTA0/pin 46, SWO on PTA2/pin 49, RESET on pin 74). Could be a 10-pin Cortex Debug connector, 2x5 shrouded header, or unpopulated pads
- Check for a UART boot mode pin — K60 supports serial bootloader via UART if NMI/FOPT bits are configured. Look for jumpers or DIP switches near the MCU
- Identify the boot flash at 0x00000-0x0FFFF — is it internal to the K60 or an external SPI flash? Check for small SOIC-8 flash chips near MCU
Firmware Extraction via SWD
Serial bootloader has no interactive mode (confirmed: no interrupt window,
no baud rate trick, binary-only status bytes 01 00 and 98 80 96). SWD
is the primary extraction path.
Equipment: SWD probe (ST-Link V2, J-Link, or similar) + 4 jumper wires.
Connections (minimum 3 wires + GND):
| Signal | K60 Pin | LQFP-144 | Notes |
|---|---|---|---|
| SWDIO | PTA3 | Pin 50 | Bidirectional data |
| SWDCLK | PTA0 | Pin 46 | Clock (probe drives) |
| GND | — | Multiple | Common ground with probe |
| RESET | — | Pin 74 | Optional but recommended |
| SWO | PTA2 | Pin 49 | Optional trace output |
Step 1: Check flash security (CRITICAL — do this first)
# pyocd (recommended)
pyocd cmd -t mk60dn512xxx10 -c "read32 0x40C"
# or openocd
openocd -f interface/stlink.cfg -f target/k60.cfg \
-c "init; halt; mdw 0x40C; exit"
The Flash Security register (FTFL_FSEC) at 0x40C:
- Bits [1:0] =
10→ SECURED — SWD reads blocked, need EzPort fallback - Bits [1:0] =
00or11→ UNSECURED — full flash dump possible
Also check 0x400 (FTFL_FOPT / Flash Configuration Field at 0x400-0x40F):
pyocd cmd -t mk60dn512xxx10 -c "read8 0x400 16"
This 16-byte field (programmed into flash at 0x400) controls boot security, backdoor key, mass erase enable, and other flash protection settings.
Step 2: Dump firmware (if unsecured)
# Full 512KB flash dump (bootloader + application)
pyocd flash -t mk60dn512xxx10 -r firmware_full.bin \
--address 0x00000 --size 0x80000
# Or just the application (448KB)
pyocd flash -t mk60dn512xxx10 -r firmware_app.bin \
--address 0x10000 --size 0x70000
# Also dump the 128KB RAM (may contain runtime state)
pyocd cmd -t mk60dn512xxx10 \
-c "savemem 0x1FFF0000 0x20020000 sram_dump.bin"
Step 3: If flash is secured — EzPort fallback
EzPort is an SPI-based flash interface activated by holding PTA4 (pin 51) low during reset. It may bypass flash security for reads on some K60 silicon revisions (Rev 2.4 / mask 4N22D — check errata).
EzPort SPI wiring:
EZP_CS = PTA4 (pin 51) — hold LOW during reset to enter EzPort
EZP_CLK = PTA0 (pin 46) — same pin as SWDCLK!
EZP_DOUT = PTA2 (pin 49) — same pin as SWO
EZP_DIN = PTA1 (pin 47)
EzPort commands: 0x03 = Read, 0x05 = Read Status, 0x0B = Fast Read.
Use an SPI master (ESP32, RPi, FTDI MPSSE) at ≤ 4 MHz.
Flash layout (from os id):
0x00000 - 0x0FFFF Bootloader v1.01 (64KB)
0x10000 - 0x7FFFF Application v02.02.48 (448KB)
Total: 512KB (0x80000)
Boot sequence bytes (from serial capture):
reboot echo → 0x01 0x00 (binary status) → "Bootloader version: 1.01"
→ "Application is running..." → 0x98 0x80 0x96 (binary, meaning unknown)
→ "Application Starting Kinetis PCB..."
The 0x01 0x00 and 0x98 0x80 0x96 are binary protocol bytes between
bootloader and application — possibly a handshake or integrity check.
Understanding these requires disassembling the bootloader.
Component Identification
- Photograph both sides of the PCB (high-res, good lighting)
- Read markings on the BCM4515 DVB tuner IC — confirm package, check for additional Broadcom support ICs nearby
- Identify the two A3981 stepper driver ICs (TSSOP-28 with exposed pad, should be near motor connectors). Note their orientation and surrounding passives (sense resistors for current measurement)
- Read crystal oscillator marking — frequency determines PLL configuration (K60 accepts 3-32 MHz, likely 8 MHz or 16 MHz for clean 96 MHz PLL)
- Check for external EEPROM (separate from K60 internal flash) — likely I2C EEPROM near MCU, possibly AT24Cxx or M24Cxx series
- Look for LNB voltage regulator circuit (13V/18V switching) — probably near the coax F-connector
Gyro / IMU Investigation
- NVS indices 72-77 configure gyro parameters (sensitivity, filter, mount type) but boot log says nothing about a gyro. Check if there's an onboard MEMS gyro or an unpopulated footprint for one
- If present, identify the gyro IC (likely single-axis rate gyro given the NVS parameters — "Gyro Mount Type: 1" suggests a specific axis)
- If unpopulated: the G2 may share a PCB design with a larger model (Trav'ler SK-1000?) that includes a gyro for in-motion tracking
GPS Investigation
- Boot log says "GPS Not Found" — look for a GPS module footprint (populated or empty). NVS 63-64 configure GPS heading/moving thresholds
- Check for a GPS antenna connector (SMA or U.FL) or ceramic patch antenna on the PCB
- If unpopulated: same shared-PCB theory as the gyro — the G2 firmware has GPS support but the hardware may be DNP (Do Not Populate)
Motor / Drive Train
- Trace SPI bus from K60 to A3981 drivers — identify which DSPI peripheral (DSPI0, DSPI1, or DSPI2) connects to which axis (AZ vs EL)
- Identify motor sense resistors near A3981 chips — value determines current limit calibration. Compare with NVS current limit hex values (indices 95-98, 110-111)
- Check motor connectors — are they direct stepper connections (4-wire) or do they go through additional driver stages?
- Look for limit switches or optical encoders (the G2 uses stall detection for homing, but there may be unused provisions for position feedback)
RF Signal Path
- Trace signal path: F-connector → LNB bias tee → BCM4515 RF input
- Identify any bandpass filters, LNAs, or frequency conversion stages between the coax and the BCM4515
- Check if the ADC RSSI signal is a dedicated analog output from BCM4515 or if it's derived from an AGC control voltage
Power Supply
- Identify main voltage rails — the board likely has 12V input (from RP-SK87 PSU), 5V (for logic), 3.3V (K60 core), and motor supply
- Check if the USB VREG (K60 pins 21-22) is connected to anything or if VOUT33 is tied to the board's 3.3V rail directly
- Identify motor power supply — A3981 supports up to 28V, the board likely runs motors at 12V from the main supply
TODO: Firmware Exploration (Serial Console)
Remaining commands to test via the RS-422 console.
DVB Submenu (Task #13) — COMPLETE
ls— streaming transponder scan (exits DVB submenu on interrupt!)qls— streaming quick lock status (~100ms:Lock:0 rssi:500 cnt:0)snr— streaming SNR readings (transient spikes in noise floor)diag— multi-block one-shot per-transponder diagnosticstable— full 32-transponder scan (~136s), detailed per-xp datafreqs— frequency list name ("Non-Stacked")stats— streaming, silent when no locknid— streaming network ID (FFFF/none), CR-overwrite displaydi2id/di2stat/di2conf/di2sc/di2rcs— all fail (no switch)di2cs— needs parameters (switch config)send— needs 3-6 hex bytes (raw DiSEqC packet)h 1-13— full parameter help documentedtabto/to— timeout configs (table vs single tune)ovraddr/rrto/tdthresh/pretx— DiSEqC timing paramssrch/shuf/tablex— toggle commandspwr— power state,srch_mode— search mode,range— scan rangedef— silent defaults reset (dangerous!),msw— format unknowne <n> <v>— edit channel params (not tested — would modify state)send <hex>— raw DiSEqC (not tested — no switch connected)
MOT Submenu (Task #14) — COMPLETE
- Full
?help listing — 25 commands discovered (see Motor Control section) asupports relative moves with+/-prefix (undocumented upstream!)h *homes both motors simultaneouslyl— lists 2 motors: 0=AZIMUTH (local), 1=ELEVATION (local)ma/mv— read motor dynamics (400 deg/s², 65/45 deg/s AZ/EL)p— read step positions (19998 AZ, 3116 EL)elminmaxhome— EL min=18°, max=65°, home=65°ela2s/els2a— angle↔step conversion (linear mapping confirmed)e/r— engage/release motorsazscan/azscanwxp— sky scan commands documentedsd— stall detection documentedlife/motorlife/motorboth— factory life test commandspid,sp,sw,w,v,vms— write-only commands documentedg <az> <el>— NOT available on G2 (not in help listing)- Test
h 0andh 1homing (requires clear space around dish) - Test
azscanwxpwith real scan parameters (RF imaging)
NVS Experiments (Caution)
- NVS 2 ("Debug 2nd Console Port") — try setting to 1, check if USB console activates. Save original value first, restore after test
- NVS 4 ("Debug Port Connection") — similar test
- NVS 112 ("Disable Dipswitch?") — what happens if dipswitch is disabled?
- NVS 38 ("Sleep Mode Timer") — currently 420s (7 min). Set higher to prevent sleep during long tracking sessions
A3981 Submenu (Task #16) — COMPLETE
- Full
?help listing — 6 commands:diag,sm,ss,st,cm,reset diag— AZ/EL both OK (no faults)sm— step size mode: both AUTOss— step size: both 1 (1/16 microstepping, finest)st— torque: both LOW (idle)cm— current control mode: both AUTO- Test
diagoutput under motor load (move motor, read diag simultaneously) - Test changing step mode from AUTO to manual
reset— clear fault flags (no faults to clear currently)
ADC Submenu (Task #16) — COMPLETE
- Full
?help listing — 5 commands:bdid,bdrevid,rssi,m,scan bdid→ STATIONARY,bdrevid→ Arssi→ 232 (noise floor baseline)scan— streaming: position + RSSI + Lock + SNR + delta per samplem— returns immediately (toggle for background monitoring?)
Dipswitch Submenu (Task #16) — COMPLETE
- Full
?help listing — 1 command:dipswitch dipswitch→ val:ffffff01, app_dipswitch:101
GPIO Submenu (Task #16) — COMPLETE
- Full
?help listing — 4 commands:regs,r,w,dir regs— full 101-pin dump (5 ports, all pin states)r <port> <pin>/w <port> <pin>/dir <bit> <val>— help documented- Map specific GPIO pins to hardware functions (SPI→A3981, UART, I2C, etc.)
LATLON Submenu (Task #16) — COMPLETE
- Full
?help listing — 1 command:l <satlon1> <satlon2> <satel1> <satel2> - Tested with satellite positions — self-localization algorithm confirmed
OS Submenu (Task #16) — COMPLETE
- Full
?help listing — 2 commands:id,reboot(notasks/killon G2!) id— full system identification including NVS version, MCU details, clock speeds- Deep probe:
go/date/timeinitially appeared as hidden commands but were confirmed as false positives — boot sequence output captured afterrebootcaused a system restart mid-probe. All three return "Invalid command" post-boot.
PEAK Submenu (Task #16) — COMPLETE
- Full
?help listing — 6 commands:ts,pw,psnr,pxy1,stb,rssits stb— LNB polarity switching test (18V=504 RSSI, 13V=232 RSSI)rssits— streaming every 1 min, exits submenu on interruptpw/psnrhelp — both take<az_deg> <el_deg>(signal peaking algorithms)pxy1help — takes<n>repeat count (2D cross-pattern peak search)- Test
pw/psnr/pxy1with a real satellite signal
EEPROM Submenu (Task #15) — COMPLETE
- Full
?help listing — 3 commands:ee,inv,def - Complete EEPROM dump (all 17 indices, 0-16) via
scripts/ee_dump.py - Bounds discovery: firmware enforces Min:0 Max:16
- Sentinel value: 0x00010101 for invalid/uninitialized entries
- Only 6 indices (0-5) ever written; 6-16 factory default (invalid)
def— restore factory defaults (not tested — would reset all values)- Determine meaning of valid EEPROM values (indices 1-5)
- Restore index 0 via
deforee 0 <value>if original value is discovered
STEP Submenu (Task #16) — COMPLETE
e— engage motors ("Motors engaged"), verified via MOTr— release motors (documented via MOT, not tested to avoid losing position)p— read step positions (AZ=19998, EL=3116)ma/mv— read accel and velocity in raw microstep unitspid— readable in STEP (write-only in MOT): Kp=250, Kv=50- MOT↔STEP conversion table verified (linear mapping)
p <motor> <pos>— test absolute microstep move (40000 steps/rev AZ, 24960 EL)v <motor> <vel>— test continuous velocity mode
Hidden Command Deep Probe — COMPLETE
Systematic brute-force probe of 415 candidate commands across all 13 menu levels
(root + 12 submenus). Script: scripts/hidden_menu_probe.py --deep
Results by submenu:
| Submenu | Probed | Hits | Known | New | Notes |
|---|---|---|---|---|---|
| TRK> | 415 | 3 | 3 | 0 | Only q, Q, help |
| OS> | 415 | 7 | 4 | 0 | go/date/time were false positives (see below) |
| MOT> | 415 | 22 | 22 | 0 | All in help listing already |
| DVB> | 415 | 12 | 12 | 0 | All previously discovered |
| STEP> | 415 | 12 | 12 | 0 | Mirrors MOT (engage/release/pos/vel) |
| A3981> | 415 | 5 | 5 | 0 | All in help listing already |
| ADC> | 415 | 7 | 7 | 0 | |
| GPIO> | 415 | 7 | 7 | 0 | |
| PEAK> | 415 | 3 | 3 | 0 | |
| NVS> | 415 | 412 | n/a | 0 | False positives: auto-advance cursor |
| EE> | 415 | 3 | 3 | 0 | |
| LATLON> | 415 | 5 | 5 | 0 | |
| DIPSWITCH> | 415 | 3 | 3 | 0 |
OS false positives explained: The probe hit reboot early in the OS
sequence, causing a full system restart (~10s). The probe script continued
sending commands into the boot stream. go, date, and time appeared as
"hits" because their responses contained non-error text — but this text was
unsolicited boot output, not command responses:
go"response" = SPI2 initialization output (BCM4515 firmware transfer at boot)date"response" = BCM4515 hardware init (AP RAM FW VERIFIED, chip IDs)time"response" = DVB channel init (dvbPrvChangeChannelInit Complete)
All three return "Invalid command" when tested manually in the OS submenu after boot completes. The boot output is still valuable — it reveals the SPI2 clock (6,857,142 Hz = 48 MHz / 7), SPI mode 3 (CPOL=1, CPHA=1), and the full BCM4515 bring-up sequence.
NVS false positives: The NVS submenu returned 412 hits because its parser
treats any unrecognized input as "read next entry" — an internal cursor
auto-advances through the NVS table. These are NOT hidden commands, just the
fallthrough behavior of the NVS e (edit/read) command parser.
Conclusion: Zero genuinely hidden commands found across all 13 menu levels. The G2 firmware shell is well-scoped — no memory access, no flash read/dump, no hidden debug backdoors. Firmware extraction requires physical access (SWD or EzPort).