Smart Radio Telescope

Radio Telescope Mount – Project Context

Project Overview

Azimuth/elevation/polarization mount for a satellite dish used as a radio-telescope antenna. Goal: a collaborative school demonstrator project.

System architecture

┌─────────────────────────────────────────────────────┐
│                  Raspberry Pi                        │
│  - Stellarium protocol (TCP port 10001)              │
│  - Web UI (Flask/FastAPI, port 80)                   │
│  - Coordinate math (RA/Dec ↔ Az/El)                  │
│  - GNSS directly over UART                           │
│  - WiFi                                              │
│  - NTP / system time                                │
└──────────────┬──────────────────────────────────────┘
               │ UART (ASCII protocol)
               │ HOME / GOTO az el pol / STATUS / STOP
┌──────────────▼──────────────────────────────────────┐
│           Raspberry Pi Pico (RP2040)                 │
│  - Real-time motor control (3 axes)                  │
│  - Trapezoidal velocity profile                      │
│  - Homing routine (3-phase)                          │
│  - PIO for precise STEP pulse generation             │
│  - Core 0: UART protocol / Core 1: motor control     │
└──────┬──────────────┬──────────────┬─────────────────┘
       │              │              │
┌──────▼──────┐ ┌─────▼──────┐ ┌────▼───────┐
│  DRV8825    │ │  DRV8825   │ │  DRV8825   │
│  Azimuth    │ │  Elevation │ │  Polariza- │
│  Vref=0.15V │ │  Vref=0.15V│ │  tion      │
└──────┬──────┘ └─────┬──────┘ │  Vref=?    │
       │              │        └────┬───────┘
┌──────▼──────┐ ┌─────▼──────┐      │
│ 17HS010-03N │ │ 17HS010-03N│ ┌────▼───────┐
│  Azimuth    │ │  Elevation │ │  Bipolar   │
└─────────────┘ └────────────┘ │  4-wire    │
                               │  pol. motor│
                               └────────────┘

Hardware

Stepper motors

Moons’ 17HS010-03N (×2, azimuth + elevation) - NEMA 17, hybrid stepper - Step angle: 1.8° (200 steps/rev) - Current: ~0.3A per phase - 6 wires (unipolar with center taps)

Wire assignment:

Black  → Winding A end 1  (DRV8825: 1A)
Red    → Winding A end 2  (DRV8825: 1B)
White  → Center tap A     → DO NOT CONNECT
Yellow → Winding B end 1  (DRV8825: 2A)
Blue   → Winding B end 2  (DRV8825: 2B)
Orange → Center tap B     → DO NOT CONNECT

Operation: bipolar (leave center taps open) → more torque.

IMPORTANT – check coil pairs with an ohmmeter before connecting! On the 6-wire motor the coils run diagonally in the connector (not sequentially):

Coil A: Pin 1 ──── Pin 2 (center tap, open) ──── Pin 4   → ~7Ω between Pin 1 and 4
Coil B: Pin 3 ──── Pin 5 (center tap, open) ──── Pin 6   → ~7Ω between Pin 3 and 6

Measurement: ~7Ω between the ends of one coil, ∞ between coil A and B.


Bipolar motor (polarization axis, ×1) - 4 wires, bipolar, no center tap - Rated current unknown → measure Vref before commissioning! - Measure coil resistance with a multimeter (between 1A↔︎1B and 2A↔︎2B) - Conservative starting value: Vref = 0.15V, then raise gradually - Operation: directly bipolar, connect all 4 wires


Motor drivers

DRV8825 (×3, purple breakout modules — A1=azimuth, A2=elevation, A3=polarization)

  • Bipolar driver, 3.3V-logic compatible (VIH_min = 1.6V)
  • Set Vref: I_max = Vref / 0.5 → for 0.3A: Vref = 0.15V
  • R-sense on the board: R100 (0.1Ω)
  • Before commissioning: 100µF electrolytic + 100nF ceramic between VMOT and GND

Microstepping (M0/M1/M2):

M2=GND  M1=3.3V  M0=3.3V  →  1/8 step  →  1600 steps/rev  ← recommended

DRV8825 pinout:

Left (top→bottom):  DIR, STP, SLP, RST, M2, M1, M0, EN
Right (top→bottom): GND, FLT, 2A, 1A, 1B, 2B, GND, VMOT

FLT pin: on the present clone breakout boards it is not broken out — pin 2 is VDD there (3.3V logic supply). No-connect in the schematic. The DRV8825 chip has internal OCP/OTP protection and shuts down internally on a fault, but without feedback to the MCU.

Between DRV8825 and motor: nothing required. For cable length > 40cm: 33Ω series resistors on each motor line.


Endstops

  • 1× per axis (all three axes), active-LOW (switch closes to GND)
  • External pull-ups on the board (R1/R2/R3); internal pull-up additionally enabled
  • HW RC debounce (C1/C4/C5) is defective → debouncing must be done in software
  • Pins: AZ=GP14, EL=GP15, ROT=GP16 (see pin assignment above)

Status LEDs

6× LEDs each with a 330Ω series resistor to GND (3.3V logic):

LED Function
LED AZI azimuth endstop reached
LED ELE elevation endstop reached
LED POL polarization endstop reached
LED HOMING homing routine active
LED MOVING motor in motion
LED TRACKING tracking mode active

Microcontroller

Target architecture: Raspberry Pi Pico (RP2040)

  • Dual-core Cortex-M0+, 133 MHz
  • PIO (Programmable I/O) for hardware STEP pulse generation
  • 264KB RAM, 2MB flash, built-in USB
  • 3.3V logic → directly compatible with the DRV8825

Pico support circuit:

VSYS  → 5V (USB or external 5V)
3V3   → 3.3V output for logic (DRV8825 VDD, SLP, M0/M1)
GND   → GND
USB   → built in, no external chip needed
SWD   → 3-pin header (SWDIO, SWDCLK, GND) for J-Link debugging

Hard-wired on the board:

DRV8825 SLP (all 3) → 3.3V   (never sleep)
DRV8825 M0  (all 3) → 3.3V   ┐
DRV8825 M1  (all 3) → 3.3V   ├ 1/8 step fixed
DRV8825 M2  (all 3) → GND    ┘

Pico GPIO pin assignment (authoritative — from the KiCad netlist hardware/motor/):

GP0   UART0 TX → Raspberry Pi RX
GP1   UART0 RX ← Raspberry Pi TX

Axis    DIR   STEP  RST   EN    SW (endstop, active-LOW)
AZ      GP6   GP7   GP8   GP9   GP14
EL      GP10  GP11  GP12  GP13  GP15
ROT     GP5   GP4   GP3   GP2   GP16     (ROT = polarization / feed rotation)

GP26  ADC0 = AnalogIN1     GP27  ADC1 = AnalogIN2     GP28  ADC2 = AnalogIN3
SDA / SCL   I2C (connector J8 / J11)
GP25  onboard LED (heartbeat / debug)
  • EN and RST are active-LOW; RST must be driven actively HIGH (reset released) — glitch-free: gpio_put BEFORE gpio_set_dir.
  • Endstops are active-LOW; the HW RC debounce on the board is defective → software debounce required.
  • ROT is wired in descending order (EN=GP2, RST=GP3, STEP=GP4, DIR=GP5).

firmware/pico_motor/config.h was corrected to this netlist on 2026-06-25 (pins + RST + DIR invert + step limits). Earlier pin lists (AZI=GP2/3/4…) are obsolete.


Axis calibration (bench bring-up, 2026-06-25)

Bench-measured with firmware/bringup_console/ (steps at 1/8 microstep = 1600/rev):

Axis DIR flip vs. default Travel after homing (steps) Note
AZ flipped 0 … 16500
EL flipped 0 … 5800
ROT no flip 0 … 22000 high gear reduction → homing backoff 2×

The max values are hard limits (do not exceed). DIR flips and soft limits are stored in the production firmware (config.h). Bring-up homing constants: fast 640 / slow 160 / timeout 64000, backoff 400 (AZ/EL) or 800 (ROT).

Division of labor (decided 2026-06-25): the MC keeps the degree interface (GOTO/POS in degrees) and converts degrees↔︎steps itself — via a two-point calibration (*_STEPS_PER_DEG + *_HOME_OFFSET_DEG = angle at the endstop), no longer via gear ratios. The RasPi does the coordinate math (RA/Dec→Az/El) and sends degrees. steps = (deg − offset)·steps_per_deg. Calibration values still TODO (measure at two known angles per axis).


GNSS receiver

  • NMEA 0183 over UART, 9600 baud (typically NEO-6M/7M/8M/M10)
  • Provides: UTC time + date, lat/lon, altitude, satellite count, HDOP
  • Replaces NTP and hard-coded coordinates
  • On the final board: directly on the Raspberry Pi UART

Wiring:

GNSS TX → RPi/ESP32 RX
GNSS RX → RPi/ESP32 TX (optional)
GNSS VCC → 3.3V
GNSS GND → GND

Raspberry Pi

  • Handles: Stellarium protocol, web UI, coordinate math, GNSS, WiFi
  • Communicates with the Pico over UART (ASCII protocol)
  • Python software (still to be written)

Communication protocol RPi → Pico (UART, ASCII)

The complete, authoritative protocol reference is in firmware/pico_motor/UART_PROTOCOL.md — transport (115200 8N1, CR/LF tolerance), commands (HOME, GOTO, MOVE, STOP, STATUS, SLEEP/WAKE, ENABLE/DISABLE, SET ADC_RATE), telemetry/events (POS, ADC, EVT), and the HOMED lifecycle.


Coordinate math

Local sidereal time (LST)

jd  = 2440587.5 + unix_timestamp / 86400.0
T   = (jd - 2451545.0) / 36525.0
gst = 280.46061837 + 360.98564736629*(jd-2451545) + 0.000387933*-/38710000
lst = (gst + lon_deg) % 360.0  # in degrees, then → radians

Equatorial → Horizontal

ha  = lst - ra
alt = asin(sin(dec)*sin(lat) + cos(dec)*cos(lat)*cos(ha))
az  = acos((sin(dec) - sin(alt)*sin(lat)) / (cos(alt)*cos(lat)))
if sin(ha) > 0: az = 2π - az

Horizontal → Equatorial

dec = asin(sin(alt)*sin(lat) + cos(alt)*cos(lat)*cos(az))
ha  = acos((sin(alt) - sin(dec)*sin(lat)) / (cos(dec)*cos(lat)))
if sin(az) > 0: ha = -ha   # eastern sky
ra  = (lst - ha) % 2π

Stellarium Telescope Protocol (TCP, binary, little-endian)

Client → Server (GOTO, 20 bytes):

[0-1]   uint16  length = 20
[2-3]   uint16  type   = 0
[4-11]  int64   timestamp µs (ignored)
[12-15] uint32  RA   (0=0h, 0xFFFFFFFF≈24h, full circle=2^32)
[16-19] int32   Dec  (+0x40000000=+90°, -0x40000000=-90°)

Server → Client (CURRENTPOS, 24 bytes):

[0-1]   uint16  length = 24
[2-3]   uint16  type   = 0
[4-11]  int64   server timestamp µs
[12-15] uint32  RA
[16-19] int32   Dec
[20-23] int32   status = 0 (OK)

Encoding:

RA  uint32 = ra_rad  / (2π)   * 4294967296
Dec int32  = dec_rad / (π/2)  * 1073741824

Existing code files

1. telescope_mount.ino

ESP32 standalone (no WiFi): - StepperAxis class with trapezoidal profile - Homing (3-phase) - Serial interface: home, goto az el, pos, stop, sleep, wake

2. telescope_mount_wifi.ino

ESP32 with WiFi + Stellarium + web UI: - Everything from version 1 - WiFi + NTP - HTTP web UI (port 80) - Stellarium TCP server (port 10001) - RA/Dec ↔︎ Az/El coordinate conversion

3. telescope_mount_gnss.ino

ESP32 with GNSS + WiFi + Stellarium + web UI: - Everything from version 2 - TinyGPS++ library (UART2, GPIO16/17) - GNSS replaces NTP and hard-coded coordinates - System time set via settimeofday() from GPS - GNSS status in the web UI (fix, lat/lon, alt, sats, HDOP) - NTP as a fallback when there is no GNSS fix

Firmware status: - ✅ Pico firmware written: firmware/pico_motor/ (3 axes, UART protocol, PIO, ADC) — compiles. config.h brought to the correct netlist + bring-up calibration (pins, RST, DIR invert, step limits) on 2026-06-25. - ✅ Bench bring-up tool: firmware/bringup_console/ (interactive USB-CDC console, see its README). - ✅ Endstop bring-up tool: firmware/bringup_endstops_uart/ (reports debounced endstop state over UART with all drivers disabled). - ✅ Bench hardware bring-up verified 2026-07-04 — UART protocol (both directions), all 3 endstops, per-axis + combined homing, multi-axis GOTO, and SCAN in both sweep-axis roles. Full protocol: docs/test-logs/2026-07-04-motor-controller-bench-test.md. - ⬜ Raspberry Pi Python software (Stellarium + UART + web UI) — still to be written.


Motion Parameters (all three .ino files)

MICROSTEPS      = 8          // M0=H M1=H M2=L
STEPS_PER_REV   = 1600       // 200 * 8
MIN_SPEED_SPS   = 80         // start/stop speed
MAX_SPEED_SPS   = 1600       // 1 rev/s cruise
PIO_BLOCK_STEPS = 25         // speed-update granularity (ramp/block levels)
AZ/EL/POL_ACCEL_STEPS = 200  // per-axis ramp length (was global 800; 1/4 since 2026-07-15)
HOMING_SPEED    = 320        // 0.2 rev/s
HOMING_TIMEOUT  = 32000      // 20 revolutions max
HOMING_BACKOFF  = 200        // steps after first endstop contact

// Step<->degree calibration (two-point linear) — replaces gear ratios:
AZ/EL/POL_STEPS_PER_DEG   = STEPS_PER_REV/360  // TODO: measure per axis
AZ/EL/POL_HOME_OFFSET_DEG = 0.0f               // TODO: angle at the endstop
AZ/EL/POL_MAX_STEPS       = 16500 / 5800 / 22000  // hard travel limits

KiCad Board (in progress)

KiCad version: 8.0.9 on Ubuntu 24.04

Current state: - Root sheet present with 2× DRV8825 + capacitors - Second sheet created - STM32F411CEUx symbol: library MCU_ST_STM32F4STM32F411CEUx

Planned sheet structure:

Root
├── power.kicad_sch        – 12V input, decoupling caps, 3.3V LDO
├── pico.kicad_sch         – Raspberry Pi Pico, SWD header, UART connector
├── motor_azi.kicad_sch    – DRV8825 AZI + motor connector + caps
├── motor_ele.kicad_sch    – DRV8825 ELE + motor connector + caps
├── motor_pol.kicad_sch    – DRV8825 POL + motor connector + caps
└── leds.kicad_sch         – 6× status LED + 330Ω series resistors

Useful KiCad shortcuts:

A          Add symbol
S          Add sub-sheet (drag a rectangle!)
E          Properties of the selected element
P          Draw wire
Q          Add power symbol
R          Rotate symbol
G          Grab wire (moves connected wires along)

Open items / Next Steps

Hardware / KiCad

Software

    • UART protocol handler (Core 0)
    • StepperAxis class with trapezoidal profile (Core 1)
    • PIO for STEP pulse generation
    • Homing routine with internal pull-up
    • Status-LED control
    • Serial interface to the Pico
    • Stellarium TCP server (port 10001)
    • GNSS (pyserial + pynmea2)
    • Coordinate math
    • Web UI (FastAPI + uvicorn recommended)

Motion / Noise

Test sequence

  1. Pico blink test (verify the toolchain) ✅
  2. Single-axis stepper test (1 DRV8825) ✅
  3. Set Vref (multimeter at TP) → 0.15V for 0.3A
  4. Verify the homing routine (endstops) ✅ (2026-07-04; endstops + per-axis & combined homing)
  5. All 3 axes simultaneously ✅ (2026-07-04; combined HOME + concurrent multi-axis GOTO + SCAN)
  6. Test the RPi ↔︎ Pico UART protocol ✅ (2026-07-04; protocol verified via USB-TTL adapter — RPi backend integration still pending)
  7. Connect Stellarium and test GOTO
  8. Verify the GNSS fix

Bench-test details for steps 4–6: docs/test-logs/2026-07-04-motor-controller-bench-test.md


Dependencies / Libraries

Arduino (ESP32)

TinyGPS++     – GNSS NMEA parser (Arduino Library Manager)
WebServer.h   – included in the ESP32 Arduino core
WiFi.h        – included in the ESP32 Arduino core

Pico C SDK

pico_stdlib   – GPIO, UART, timing
hardware_pio  – PIO for STEP pulse generation
hardware_uart – UART0 to the Raspberry Pi
pico_multicore – dual-core (Core 0 / Core 1)

Python (Raspberry Pi, still to install)

pyserial      – UART to the Pico
pynmea2       – GNSS NMEA parser
fastapi       – web UI
uvicorn       – ASGI server
astropy       – optional, for more precise coordinate math

References

  • DRV8825 datasheet: https://www.ti.com/product/DRV8825
  • RP2040 datasheet: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
  • Stellarium Telescope Protocol: https://github.com/Stellarium/stellarium/blob/master/plugins/TelescopeControl/src/TelescopeClient.hpp
  • Moons 17HS010-03N: NEMA17 hybrid stepper, 0.3A, 1.8°/step ```
Back to top