#alpaca #astronomy #astrophotography #api-bindings #ascom

ascom-alpaca

Rust implementation of the ASCOM Alpaca API for astronomy devices

12 releases

Uses new Rust 2024

new 1.0.0-beta.10 Jan 13, 2026
1.0.0-beta.8 Jan 11, 2026
1.0.0-beta.7 Dec 28, 2025
1.0.0-beta.6 Sep 9, 2025
1.0.0-beta.1 May 27, 2023

#278 in Hardware support

Download history 2/week @ 2025-09-18 8/week @ 2025-09-25 13/week @ 2025-10-02 1/week @ 2025-10-09 29/week @ 2025-10-16 180/week @ 2025-10-23 21/week @ 2025-10-30 6/week @ 2025-11-20 1/week @ 2025-12-11 260/week @ 2025-12-18 617/week @ 2025-12-25 162/week @ 2026-01-01

1,040 downloads per month
Used in qhyccd-alpaca

MIT/Apache

280KB
5K SLoC

ascom-alpaca-rs

This is a Rust implementation of the standard ASCOM Alpaca API for astronomy devices.

It implements main Alpaca API clients and servers, as well as transparent support for auto-discovery mechanism and ImageBytes encoding for camera images.

The usage documentation lives at docs.rs.


lib.rs:

This is a Rust implementation of the standard ASCOM Alpaca API for astronomy devices.

It implements main Alpaca API clients and servers, as well as transparent support for auto-discovery mechanism and ImageBytes encoding for camera images.

Compilation features

This crate defines two sets of compilation features that help to keep binary size & compilation speed in check by opting into only the features you need.

First set is along the client-server axis:

  • client: Enables client-side access to Alpaca-capable devices.
  • server: Allows to expose your own devices as Alpaca servers.

The second set of features is based on the device type and enables the corresponding trait:

  • all-devices: Enables all of the below. Not recommended unless you're building a universal astronomy application.
  • camera: Enables support for cameras via the Camera trait.
  • cover_calibrator: Enables [...] the CoverCalibrator trait.
  • dome: Enables Dome.
  • filter_wheel: Enables FilterWheel.
  • focuser: Enables Focuser.
  • observing_conditions: Enables ObservingConditions.
  • rotator: Enables Rotator.
  • switch: Enables Switch.
  • telescope: Enables Telescope.

Once you decided on the features you need, you can add this crate to your Cargo.toml. For example, if I'm implementing an Alpaca camera driver, I'd add the following to my Cargo.toml:

[dependencies]
ascom-alpaca = { version = "0.1", features = ["client", "camera"] }

ascom-alpaca also provides a test feature that enables testing utilities for both clients and servers. See the corresponding section below for details.

Device methods

All the device type trait methods are async and correspond to the ASCOM Alpaca API. They all return ASCOMResult<...>.

The Device supertrait includes "ASCOM Methods Common To All Devices" from the Alpaca API, as well as a few custom metadata methods used for the device registration:

Implementing a device server

Since async traits are not yet natively supported on stable Rust, the traits are implemented using the async-trait crate. Other than that, you should implement trait with all the Alpaca methods as usual:

use ascom_alpaca::ASCOMResult;
use ascom_alpaca::api::{Device, Camera};
use async_trait::async_trait;

#[derive(Debug)]
struct MyCamera {
// ...
}

#[async_trait]
impl Device for MyCamera {
fn static_name(&self) -> &str {
"My Camera"
}

fn unique_id(&self) -> &str {
"insert GUID here"
}

// ...
}

#[async_trait]
impl Camera for MyCamera {
async fn bayer_offset_x(&self) -> ASCOMResult<i32> {
Ok(0)
}

async fn bayer_offset_y(&self) -> ASCOMResult<i32> {
Ok(0)
}

// ...
}

Any skipped methods will default to the following values:

Once traits are implemented, you can create a server, register your device(s), and start listening:

use ascom_alpaca::Server;
use ascom_alpaca::api::CargoServerInfo;
use std::convert::Infallible;

// ...implement MyCamera...
#

#[tokio::main]
async fn main() -> eyre::Result<Infallible> {
// create with the helper macro that populate server information from your own Cargo.toml
let mut server = Server::new(CargoServerInfo!());

// By default, the server will listen on dual-stack (IPv4 + IPv6) unspecified address with a randomly assigned port.
// You can change that by modifying the `listen_addr` field:
server.listen_addr.set_port(8000);

// Create and register your device(s).
server.devices.register(MyCamera { /* ... 

Dependencies

~11–32MB
~358K SLoC