Skip to content

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.

Before Craft can move your hardware, you need:

  1. A rotor with Hamlib support — any rotator supported by Hamlib will work. This includes Yaesu G-5500, SPID, Green Heron, and many others.
  2. rotctld running — the Hamlib daemon that exposes your rotor over TCP. Start it with something like:
Terminal window
rotctld -m 603 -r /dev/ttyUSB0 -s 9600 -t 4533

The flags vary by rotor model (-m is the Hamlib model number). Check rotctld --list for your hardware.

Craft needs to know where rotctld is listening. Set these in your .env:

Terminal window
ROTOR_DEFAULT_HOST=127.0.0.1
ROTOR_DEFAULT_PORT=4533
ROTOR_MIN_ELEVATION=5.0

You can also register multiple rotors through the API:

Terminal window
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:

Terminal window
curl "https://your-domain/api/rotors"

Craft communicates with rotctld using a simple text-based protocol over TCP. Each command is a single line; each response ends with a newline.

CommandDescriptionExample response
pGet current position180.0\n45.0\n (azimuth, elevation)
P <az> <el>Set positionRPRT 0 (success)
SStop all movementRPRT 0
_Get rotor model nameYaesu GS-232B
qQuit / close connection

Craft also supports extended protocol commands for hardware that implements them:

CommandDescription
R <n>Read RSSI (signal strength) with n iterations
LEnable LNA
DDiscover supported capabilities

The client supports both stateless mode (one TCP connection per command) and persistent connections for batch operations like sky surveys.

To point your rotor at a specific object:

  1. Find the target — search for it by name or NORAD ID
  2. Check it is above the horizon — Craft refuses to point at targets below the horizon
  3. Send the point command
Terminal window
# Point rotor 1 at a satellite
curl -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.

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:

Terminal window
# Start tracking the ISS with rotor 1
curl -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 P commands 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
Terminal window
# Stop tracking and halt the rotor
curl -X POST "https://your-domain/api/rotors/1/stop"
# Or just end the tracking session without stopping movement
curl -X DELETE "https://your-domain/api/rotors/1/track"

Each rotor has a configurable park position (defaults to 0 degrees azimuth, 0 degrees elevation). Park your rotor when you are done operating:

Terminal window
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.

Before tracking real targets, verify that Craft can talk to your rotctld instance:

Terminal window
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.internal or the host’s IP)
  • The rotctld model number matches your hardware

Craft logs every tracking session. View recent sessions for a rotor:

Terminal window
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.