ST4-ESP32

ESP32 library for controlling telescope mounts via the ST-4 autoguider port. Drives optocoupler-isolated GPIO pins with hardware-timer pulse guiding, microsecond position tracking, and FreeRTOS thread safety.

Supports serial, WebSocket, and ASCOM Alpaca interfaces -- drop-in compatible with the original arduino-st4 protocol while adding pulse guiding, position tracking, and network control.

Wiring

ESP32                TLP521-4 (Quad Optocoupler)              RJ12 (ST-4)
                    ┌──────────────────────┐
GPIO 16 (RA+)  ──┤1  Anode    Collector 8├── Pin 3 (RA+)
              GND ──┤2  Cathode   Emitter 7├── Pin 2 (GND)
GPIO 17 (RA-)  ──┤3  Anode    Collector 6├── Pin 5 (RA-)
              GND ──┤4  Cathode   Emitter 5├── Pin 4 (DEC-)
                    └──────────────────────┘

                    ┌──────────────────────┐
GPIO 18 (DEC+) ──┤1  Anode    Collector 8├── Pin 6 (DEC+)
              GND ──┤2  Cathode   Emitter 7├── Pin 2 (GND)
GPIO 19 (DEC-) ──┤3  Anode    Collector 6├── Pin 4 (DEC-)
              GND ──┤4  Cathode   Emitter 5├── Pin 2 (GND)
                    └──────────────────────┘

GPIO 2  (LED)  ── 220R ── LED ── GND

Each GPIO drives an optocoupler LED through a current-limiting resistor (220-470 ohm). The optocoupler transistor shorts the corresponding ST-4 signal to ground, simulating a button press on the hand controller. The optical isolation protects the ESP32 from the mount's electrical domain.

See arduino-st4/Hardware/ for reference photos of the original build.

Quick Start

#include <ST4.h>

ST4Controller controller;
ST4Serial serial;

void setup() {
    controller.begin();       // Default pins: 16, 17, 18, 19, LED=2
    serial.begin(controller); // 57600 baud, extended protocol
}

void loop() {
    serial.update();
}

Flash with PlatformIO:

pio run -e serial_compatible -t upload -t monitor

Features

  • GPIO safety -- mutual exclusion prevents simultaneous plus/minus activation on the same axis (optocoupler short protection)
  • Hardware-timer pulse guiding -- esp_timer one-shot with FreeRTOS task handoff, non-blocking microsecond precision
  • Dead-reckoning position tracking -- port of ASCOM AxisMovementTracker.cs using esp_timer_get_time() for microsecond resolution
  • Configurable sidereal rates -- default 9x/7x RA (accounts for Earth rotation), 8x symmetric DEC
  • Serial protocol -- backward compatible with original arduino-st4 ASCOM driver, plus extended commands
  • WebSocket server -- JSON command/state protocol with automatic state broadcasting
  • ASCOM Alpaca REST API -- standard telescope interface for N.I.N.A., PHD2, and any ASCOM-compatible software
  • FreeRTOS thread safety -- layered mutex hierarchy (Controller > Pulse > Axis) prevents deadlocks across ESP32 dual cores
  • Conditional compilation -- WiFi and Alpaca are opt-in via #define, keeping the core lean

Serial Protocol

Connect at 57600 baud, 8N1. Commands are terminated with #.

Basic Commands (arduino-st4 compatible)

Command Action
CONNECT# Enable mount control, turn on LED
DISCONNECT# Stop all axes, turn off LED
RA+# Slew RA positive
RA-# Slew RA negative
RA0# Stop RA axis
DEC+# Slew DEC positive
DEC-# Slew DEC negative
DEC0# Stop DEC axis

Extended Commands (enabled by default)

Command Response
PULSE RA+ 500# Pulse guide RA+ for 500 ms
PULSE DEC- 1000# Pulse guide DEC- for 1000 ms
POS?# POS 12.345678 45.678901#
SYNC 12.345 45.678# Set position to given RA/DEC
STATUS?# STATUS CONNECTED RA:+:12.345678 DEC:0:45.678901#
VERSION?# VERSION 2026.02.17#

WebSocket Protocol

Connect to ws://<ip>:81/ws. Commands and state are JSON.

Client to Server

{"cmd":"move","axis":"ra","dir":"+"}
{"cmd":"move","axis":"dec","dir":"-"}
{"cmd":"pulse","axis":"ra","dir":"+","ms":500}
{"cmd":"stop"}
{"cmd":"sync","ra":12.345,"dec":45.678}
{"cmd":"status"}

Server to Client (broadcast)

{
  "type": "state",
  "connected": true,
  "ra":  {"active": false, "dir": "+", "pos": 12.345678},
  "dec": {"active": true,  "dir": "-", "pos": 45.678901}
}

State broadcasts on direction change and every 250 ms during active slew.

ASCOM Alpaca

The Alpaca interface implements the ASCOM Telescope v3 specification, allowing any compatible software to control the mount over HTTP.

  • REST API on port 32323 (configurable)
  • UDP discovery on port 32227 -- clients auto-detect the device
  • CORS enabled for web-based Alpaca clients

Supported Operations

Operation Endpoint Method
Connect/Disconnect /api/v1/telescope/0/connected PUT
Pulse Guide /api/v1/telescope/0/pulseguide PUT
Move Axis /api/v1/telescope/0/moveaxis PUT
Abort Slew /api/v1/telescope/0/abortslew PUT
Sync Position /api/v1/telescope/0/synctocoordinates PUT
Get Position /api/v1/telescope/0/rightascension, declination GET
Slewing State /api/v1/telescope/0/slewing GET
Pulse Active /api/v1/telescope/0/ispulseguiding GET

PulseGuide Direction Mapping

Alpaca Direction Value ST-4 Mapping
North 0 DEC+
South 1 DEC-
East 2 RA+
West 3 RA-

Enable with #define ST4_ALPACA_ENABLED before including ST4.h, or use the alpaca_server example.

Pin Configuration

Override defaults by defining before #include <ST4.h>:

#define ST4_PIN_RA_PLUS   16  // GPIO for RA positive
#define ST4_PIN_RA_MINUS  17  // GPIO for RA negative
#define ST4_PIN_DEC_PLUS  18  // GPIO for DEC positive
#define ST4_PIN_DEC_MINUS 19  // GPIO for DEC negative
#define ST4_PIN_LED        2  // Status LED

Active logic is configurable per-axis (ACTIVE_HIGH default, ACTIVE_LOW for inverted drivers).

Rate Configuration

Default sidereal rate multipliers (from the original ASCOM driver):

Axis Direction Multiplier Effective Rate
RA Plus 9x 8x slew + 1x Earth rotation
RA Minus 7x 8x slew - 1x Earth rotation
DEC Plus 8x Symmetric
DEC Minus 8x Symmetric

Override at runtime:

ST4RateConfig rates = {9.0, 7.0, 8.0, 8.0};
controller.setRates(rates);

Examples

Example Features Build
basic_gpio Raw axis control, wiring verification make basic
serial_compatible Serial protocol, ASCOM driver compatible make serial
pulse_guide Hardware-timer pulse guiding, position tracking make pulse
wifi_control WebSocket server, JSON state broadcasting make wifi
alpaca_server ASCOM Alpaca REST API, UDP discovery make alpaca

Dependencies

Library Version Used By
ArduinoJson ^7.0.0 WiFi, Alpaca
ESPAsyncWebServer ^3.6.0 WiFi, Alpaca

Core library (serial/GPIO/pulse) has no external dependencies beyond the Arduino ESP32 framework.

Building

Requires PlatformIO.

# Build all examples
make all

# Build specific example
make serial
make wifi
make alpaca

# Run native unit tests (no hardware needed)
make test

# Upload and monitor
pio run -e serial_compatible -t upload -t monitor

# Clean build artifacts
make clean

Native Tests

61 tests across 5 suites run on the host machine without ESP32 hardware. A thin mock layer in test/mocks/ replaces Arduino, FreeRTOS, and esp_timer APIs:

test_pin        9 tests   GPIO logic, active-high/low, pin validation
test_axis      10 tests   Mutual exclusion, direction control, safety
test_tracker   11 tests   Position accumulation, rate changes, timing
test_serial    16 tests   Protocol parsing, all commands, edge cases
test_controller 15 tests  Rate calculation, connection guard, state
pio test -e native -v

Project Structure

st-4-esp32/
├── include/          Header files
│   ├── ST4.h         Facade (includes everything)
│   ├── ST4Types.h    Enums, constants, state structs
│   ├── ST4Config.h   Default pin assignments
│   ├── ST4Pin.h      Single GPIO abstraction
│   ├── ST4Axis.h     Dual-pin axis with mutual exclusion
│   ├── ST4Tracker.h  Dead-reckoning position tracker
│   ├── ST4Pulse.h    Hardware-timer pulse engine
│   ├── ST4Controller.h  High-level mount controller
│   ├── ST4Serial.h   Serial protocol handler
│   ├── ST4WiFi.h     WebSocket server (optional)
│   └── ST4Alpaca.h   ASCOM Alpaca REST API (optional)
├── src/              Implementations
├── examples/         5 Arduino sketches
├── test/
│   ├── mocks/        Arduino/FreeRTOS mock layer
│   └── test_*/       Unity test suites
├── arduino-st4/      Original project reference
├── platformio.ini
├── Makefile
└── library.json

License

LGPL-3.0-or-later

Credits

Based on arduino-st4 by Kevin Ferrare. Sidereal rate constants and ASCOM protocol from the original ArduinoST4 ASCOM driver.

Description
ESP32 ST-4 autoguider port controller with WiFi/WebSocket, ASCOM Alpaca, hardware-timer pulse guiding, and FreeRTOS thread safety
Readme 77 KiB
Languages
C++ 97.4%
C 1.5%
Makefile 1.1%