# 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: 1. **`step`** — raw microstep commands (ustep/sec, engage/release motors) 2. **`mot`** — degree-based positioning (`a `, `h `) 3. **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 ` 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 `** — Scan AZ while stepping EL from min to max angle. Parameters are relative angles and dwell delay. Used for basic signal surveys. **`azscanwxp `** — 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 [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 ` — Oscillate both axes by relative angles - `motorlife ` — Sweep a single motor between min/max angles - `motorboth ` — Exercise both motors, max 25° delta each ### Write-Only Commands These commands require parameters — no read-only mode: - `pid ` — Set PID gains (no read command) - `sp ` — Set step position counter (doesn't move motor) - `sw ` — Set wrap position - `w 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 ` (set 0 to disable NID/SD). `to` takes arguments: `to `. ### 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 `) `h ` 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 ` | 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 ` | Write | Select transponder | | `e ` | Write | Edit channel parameter | | `h ` | 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 ` | 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 ` | 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 ` | Read | Read EEPROM value at index | | `ee ` | Write | Write EEPROM value at index | | `inv []` | 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 = ` — data is trusted - **Invalid:** `Failed to read. val:` — 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 []`** — "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 `** — 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 ` | Read | Read single GPIO pin (e.g., `r A 5`) | | `w ` | Write | Write to GPIO pin (toggle) | | `dir ` | 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 ` | 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 ` | Motor+Signal | Peak wide — sweep around AZ/EL to find signal peak | | `psnr ` | Motor+Signal | Peak SNR — sweep around AZ/EL optimizing for SNR | | `pxy1 []` | 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 = 504` - `Enabled LNB STB` → vertical polarization (13V), `Odd_sig = 232` - `Ctr = 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. The `0x01` LSB indicates one switch position is active; `0xFF` bytes 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 `q` to stop, others require closing the serial port. The `ts`, `agc`, and `lnbv` commands 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)** ```bash # 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] = `00` or `11` → **UNSECURED** — full flash dump possible Also check `0x400` (FTFL_FOPT / Flash Configuration Field at 0x400-0x40F): ```bash 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)** ```bash # 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 - [x] `ls` — streaming transponder scan (exits DVB submenu on interrupt!) - [x] `qls` — streaming quick lock status (~100ms: `Lock:0 rssi:500 cnt:0`) - [x] `snr` — streaming SNR readings (transient spikes in noise floor) - [x] `diag` — multi-block one-shot per-transponder diagnostics - [x] `table` — full 32-transponder scan (~136s), detailed per-xp data - [x] `freqs` — frequency list name ("Non-Stacked") - [x] `stats` — streaming, silent when no lock - [x] `nid` — streaming network ID (FFFF/none), CR-overwrite display - [x] `di2id` / `di2stat` / `di2conf` / `di2sc` / `di2rcs` — all fail (no switch) - [x] `di2cs` — needs parameters (switch config) - [x] `send` — needs 3-6 hex bytes (raw DiSEqC packet) - [x] `h 1-13` — full parameter help documented - [x] `tabto` / `to` — timeout configs (table vs single tune) - [x] `ovraddr` / `rrto` / `tdthresh` / `pretx` — DiSEqC timing params - [x] `srch` / `shuf` / `tablex` — toggle commands - [x] `pwr` — power state, `srch_mode` — search mode, `range` — scan range - [x] `def` — silent defaults reset (dangerous!), `msw` — format unknown - [ ] `e ` — edit channel params (not tested — would modify state) - [ ] `send ` — raw DiSEqC (not tested — no switch connected) ### MOT Submenu (Task #14) — COMPLETE - [x] Full `?` help listing — 25 commands discovered (see Motor Control section) - [x] `a` supports relative moves with `+`/`-` prefix (undocumented upstream!) - [x] `h *` homes both motors simultaneously - [x] `l` — lists 2 motors: 0=AZIMUTH (local), 1=ELEVATION (local) - [x] `ma` / `mv` — read motor dynamics (400 deg/s², 65/45 deg/s AZ/EL) - [x] `p` — read step positions (19998 AZ, 3116 EL) - [x] `elminmaxhome` — EL min=18°, max=65°, home=65° - [x] `ela2s` / `els2a` — angle↔step conversion (linear mapping confirmed) - [x] `e` / `r` — engage/release motors - [x] `azscan` / `azscanwxp` — sky scan commands documented - [x] `sd` — stall detection documented - [x] `life` / `motorlife` / `motorboth` — factory life test commands - [x] `pid`, `sp`, `sw`, `w`, `v`, `vms` — write-only commands documented - [ ] `g ` — NOT available on G2 (not in help listing) - [ ] Test `h 0` and `h 1` homing (requires clear space around dish) - [ ] Test `azscanwxp` with 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 - [x] Full `?` help listing — 6 commands: `diag`, `sm`, `ss`, `st`, `cm`, `reset` - [x] `diag` — AZ/EL both OK (no faults) - [x] `sm` — step size mode: both AUTO - [x] `ss` — step size: both 1 (1/16 microstepping, finest) - [x] `st` — torque: both LOW (idle) - [x] `cm` — current control mode: both AUTO - [ ] Test `diag` output 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 - [x] Full `?` help listing — 5 commands: `bdid`, `bdrevid`, `rssi`, `m`, `scan` - [x] `bdid` → STATIONARY, `bdrevid` → A - [x] `rssi` → 232 (noise floor baseline) - [x] `scan` — streaming: position + RSSI + Lock + SNR + delta per sample - [x] `m` — returns immediately (toggle for background monitoring?) ### Dipswitch Submenu (Task #16) — COMPLETE - [x] Full `?` help listing — 1 command: `dipswitch` - [x] `dipswitch` → val:ffffff01, app_dipswitch:101 ### GPIO Submenu (Task #16) — COMPLETE - [x] Full `?` help listing — 4 commands: `regs`, `r`, `w`, `dir` - [x] `regs` — full 101-pin dump (5 ports, all pin states) - [x] `r ` / `w ` / `dir ` — help documented - [ ] Map specific GPIO pins to hardware functions (SPI→A3981, UART, I2C, etc.) ### LATLON Submenu (Task #16) — COMPLETE - [x] Full `?` help listing — 1 command: `l ` - [x] Tested with satellite positions — self-localization algorithm confirmed ### OS Submenu (Task #16) — COMPLETE - [x] Full `?` help listing — 2 commands: `id`, `reboot` (no `tasks`/`kill` on G2!) - [x] `id` — full system identification including NVS version, MCU details, clock speeds - [x] Deep probe: `go`/`date`/`time` initially appeared as hidden commands but were confirmed as **false positives** — boot sequence output captured after `reboot` caused a system restart mid-probe. All three return "Invalid command" post-boot. ### PEAK Submenu (Task #16) — COMPLETE - [x] Full `?` help listing — 6 commands: `ts`, `pw`, `psnr`, `pxy1`, `stb`, `rssits` - [x] `stb` — LNB polarity switching test (18V=504 RSSI, 13V=232 RSSI) - [x] `rssits` — streaming every 1 min, exits submenu on interrupt - [x] `pw` / `psnr` help — both take ` ` (signal peaking algorithms) - [x] `pxy1` help — takes `` repeat count (2D cross-pattern peak search) - [ ] Test `pw` / `psnr` / `pxy1` with a real satellite signal ### EEPROM Submenu (Task #15) — COMPLETE - [x] Full `?` help listing — 3 commands: `ee`, `inv`, `def` - [x] Complete EEPROM dump (all 17 indices, 0-16) via `scripts/ee_dump.py` - [x] Bounds discovery: firmware enforces Min:0 Max:16 - [x] Sentinel value: 0x00010101 for invalid/uninitialized entries - [x] 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 `def` or `ee 0 ` if original value is discovered ### STEP Submenu (Task #16) — COMPLETE - [x] `e` — engage motors ("Motors engaged"), verified via MOT - [x] `r` — release motors (documented via MOT, not tested to avoid losing position) - [x] `p` — read step positions (AZ=19998, EL=3116) - [x] `ma` / `mv` — read accel and velocity in raw microstep units - [x] `pid` — **readable in STEP** (write-only in MOT): Kp=250, Kv=50 - [x] MOT↔STEP conversion table verified (linear mapping) - [ ] `p ` — test absolute microstep move (40000 steps/rev AZ, 24960 EL) - [ ] `v ` — 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).