Rotor Control
Craft controls antenna rotors through the Hamlib rotctld protocol — a simple TCP interface for commanding azimuth/elevation positioners. You can point at any celestial object, track a satellite continuously as it crosses the sky, and park your rotor when done.
Prerequisites
Section titled “Prerequisites”Before Craft can move your hardware, you need:
- A rotor with Hamlib support — any rotator supported by Hamlib will work. This includes Yaesu G-5500, SPID, Green Heron, and many others.
- rotctld running — the Hamlib daemon that exposes your rotor over TCP. Start it with something like:
rotctld -m 603 -r /dev/ttyUSB0 -s 9600 -t 4533The flags vary by rotor model (-m is the Hamlib model number). Check rotctld --list for your hardware.
Configuration
Section titled “Configuration”Craft needs to know where rotctld is listening. Set these in your .env:
ROTOR_DEFAULT_HOST=127.0.0.1ROTOR_DEFAULT_PORT=4533ROTOR_MIN_ELEVATION=5.0You can also register multiple rotors through the API:
curl -X POST "https://your-domain/api/rotors" \ -H "Content-Type: application/json" \ -d '{ "name": "az-el", "rotor_type": "azel", "host": "127.0.0.1", "port": 4533, "min_elevation": 5.0, "park_az": 0.0, "park_el": 0.0 }'Each rotor has its own host, port, minimum elevation, and park position. List your configured rotors:
curl "https://your-domain/api/rotors"The rotctld TCP protocol
Section titled “The rotctld TCP protocol”Craft communicates with rotctld using a simple text-based protocol over TCP. Each command is a single line; each response ends with a newline.
| Command | Description | Example response |
|---|---|---|
p | Get current position | 180.0\n45.0\n (azimuth, elevation) |
P <az> <el> | Set position | RPRT 0 (success) |
S | Stop all movement | RPRT 0 |
_ | Get rotor model name | Yaesu GS-232B |
q | Quit / close connection | — |
Craft also supports extended protocol commands for hardware that implements them:
| Command | Description |
|---|---|
R <n> | Read RSSI (signal strength) with n iterations |
L | Enable LNA |
D | Discover supported capabilities |
The client supports both stateless mode (one TCP connection per command) and persistent connections for batch operations like sky surveys.
Pointing at a target
Section titled “Pointing at a target”To point your rotor at a specific object:
- Find the target — search for it by name or NORAD ID
- Check it is above the horizon — Craft refuses to point at targets below the horizon
- Send the point command
# Point rotor 1 at a satellitecurl -X POST "https://your-domain/api/rotors/1/point" \ -H "Content-Type: application/json" \ -d '{"target_type": "satellite", "target_id": "25544"}'
# Response{ "status": "ok", "target": "ISS (ZARYA)", "azimuth": 215.3, "elevation": 42.7}Craft computes the target’s current position using Skyfield, checks that it is above both the horizon and the rotor’s minimum elevation, then sends the P command to rotctld.
Live tracking mode
Section titled “Live tracking mode”For satellites and other moving targets, a single point command is not enough — the target moves across the sky and your antenna needs to follow it. Start continuous tracking:
# Start tracking the ISS with rotor 1curl -X POST "https://your-domain/api/rotors/1/track" \ -H "Content-Type: application/json" \ -d '{"target_type": "satellite", "target_id": "25544"}'While a tracking session is active:
- The server recomputes the target position and sends updated
Pcommands to rotctld - The frontend receives rotor position over WebSocket at
/ws/rotor/{rotor_id}with 2 Hz updates - Each WebSocket frame includes azimuth, elevation, connection status, and the current tracking target
Stopping a tracking session
Section titled “Stopping a tracking session”# Stop tracking and halt the rotorcurl -X POST "https://your-domain/api/rotors/1/stop"
# Or just end the tracking session without stopping movementcurl -X DELETE "https://your-domain/api/rotors/1/track"Parking the rotor
Section titled “Parking the rotor”Each rotor has a configurable park position (defaults to 0 degrees azimuth, 0 degrees elevation). Park your rotor when you are done operating:
curl -X POST "https://your-domain/api/rotors/1/park"This stops any active tracking session first, then drives the rotor to its park position.
Testing your connection
Section titled “Testing your connection”Before tracking real targets, verify that Craft can talk to your rotctld instance:
curl "https://your-domain/api/rotors/1/test"# {"connected": true, "model": "Yaesu GS-232B"}If this returns "connected": false, check that:
- rotctld is running and listening on the configured host/port
- Network connectivity exists between the Craft API container and the rotctld host (if rotctld is outside Docker, you may need
host.docker.internalor the host’s IP) - The rotctld model number matches your hardware
Tracking session history
Section titled “Tracking session history”Craft logs every tracking session. View recent sessions for a rotor:
curl "https://your-domain/api/rotors/1/sessions?limit=10"Each session records the rotor ID, target, start time, end time, and any notes. This is useful for reviewing what you tracked and when.