ascom-alpaca-core
Framework-agnostic ASCOM Alpaca protocol types and traits for Rust.
Provides the complete protocol abstraction for all 10 ASCOM device types (~220 methods) without any HTTP framework, async runtime, or networking dependency. Works on ESP32 (via esp-idf) and desktop alike.
Quick Start
Implement a device:
use *;
Core Concepts
Device Traits
Every ASCOM device type is a Rust trait extending Device. The Device trait provides common properties (name, description, connected state, etc.), and each device trait adds type-specific methods.
All trait methods have default implementations returning NotImplemented, so you only override what your hardware supports:
Response Envelopes
All Alpaca endpoints return JSON with PascalCase field names, error codes, and transaction IDs:
// Value-returning endpoint (GET)
let response = ok
.with_transaction;
// {"Value":42.5,"ErrorNumber":0,"ErrorMessage":"","ClientTransactionID":1,"ServerTransactionID":1}
// Error response
let response = from_error.with_transaction;
// {"ErrorNumber":1025,"ErrorMessage":"Temperature out of range","ClientTransactionID":1,"ServerTransactionID":1}
// Void endpoint (PUT)
let response = ok.with_transaction;
// {"ErrorNumber":0,"ErrorMessage":"","ClientTransactionID":1,"ServerTransactionID":1}
Device Registry
The registry stores heterogeneous devices and provides typed lookup. Device numbers are assigned automatically per type (first Camera = 0, second Camera = 1, etc.):
let mut registry = new;
let sm: = Boxnew;
let cam: = Boxnew;
registry.register;
registry.register;
// For management API
let devices = registry.configured_devices;
// Typed lookup
let cam = registry.get_camera?; // &dyn Camera
let sm = registry.get_safety_monitor?; // &dyn SafetyMonitor
// Generic lookup (any device type)
let dev = registry.get_device?; // &dyn Device
Error Handling
ASCOM errors are protocol-level (HTTP 200 with error in JSON body), not HTTP errors:
use ;
| Error | Code | When to use |
|---|---|---|
NotImplemented |
0x400 | Method not supported by this hardware |
InvalidValue |
0x401 | Parameter out of valid range |
ValueNotSet |
0x402 | Required value not yet assigned (e.g., target coordinates) |
NotConnected |
0x407 | Device not connected |
InvalidWhileParked |
0x408 | Telescope/dome operation while parked |
InvalidWhileSlaved |
0x409 | Operation while dome is slaved |
InvalidOperationException |
0x40B | General invalid state |
ActionNotImplemented |
0x40C | Custom action not recognized |
OperationCancelled |
0x40E | Async operation was cancelled |
DriverError |
0x500-0xFFF | Hardware-specific errors |
Discovery & Management
use *;
// UDP discovery constants
let port = DEFAULT_DISCOVERY_PORT; // 32227
let probe = DISCOVERY_PROBE; // "alpacadiscovery1"
let ipv6 = IPV6_MULTICAST; // ff12::a1:9aca
// Management API types
use *;
let description = ServerDescription ;
Transaction Tracking
let counter = new;
let server_tx = counter.next; // thread-safe atomic increment
let tracker = new;
tracker.record_client;
Device Types
| Trait | Methods | Description |
|---|---|---|
SafetyMonitor |
1 | Generic unsafe condition trigger (IsSafe) |
Switch |
16 | Multi-channel on/off and analog switches |
Camera |
~55 | Imaging with exposure, binning, gain, cooling |
CoverCalibrator |
12 | Flat panel and dust cover control |
Dome |
22 | Observatory dome rotation and shutter |
FilterWheel |
7 | Filter wheel position and naming |
Focuser |
12 | Focus motor with temperature compensation |
ObservingConditions |
15 | Weather station (13 sensors) |
Rotator |
12 | Camera rotator (mechanical + logical position) |
Telescope |
~60 | Mount control, tracking, guiding, slewing |
SafetyMonitor
A generic trigger for unsafe conditions — not just weather. Any condition that should halt imaging operations: wind, rain, cloud cover, door open, power failure, equipment malfunction, dew heater offline, or a dead man's switch timeout. Returns a single boolean: is it safe to continue?
Switch
Multi-channel device with boolean, multi-state, and analog channels:
Camera
The most complex device type. Key capability groups:
- Exposure:
start_exposure,stop_exposure,abort_exposure,image_ready,image_array - Binning:
bin_x/y,max_bin_x/y,can_asymmetric_bin - Subframe:
start_x/y,num_x/y - Cooling:
cooler_on,set_ccd_temperature,cooler_power(gated bycan_set_ccd_temperature) - Gain: Two mutually exclusive modes:
- Numeric:
gain,gain_min,gain_max—gains()returns NotImplemented - Named:
gains()returns a list —gain_min/gain_maxreturn NotImplemented
- Numeric:
- Offset: Same two modes as gain (
offset/offset_min/offset_maxvsoffsets()) - Pulse guide:
pulse_guide,is_pulse_guiding(gated bycan_pulse_guide) - Image data:
ImageDataenum with 2D/3D i32/i16 arrays, plus ImageBytes binary encoding
Telescope
The most method-rich device (~60 methods). Key areas:
- Coordinates:
right_ascension,declination,altitude,azimuth,sidereal_time - Slewing:
slew_to_coordinates[_async],slew_to_alt_az[_async],slew_to_target[_async] - Tracking:
tracking,set_tracking,tracking_rate,right_ascension_rate,declination_rate - Guiding:
pulse_guide,guide_rate_right_ascension/declination - German equatorial:
side_of_pier,set_side_of_pier,destination_side_of_pier - Site:
site_latitude,site_longitude,site_elevation
Important ASCOM semantics:
- Sidereal tracking does NOT change the reported RA — the mount compensates for Earth's rotation
- Only
RightAscensionRate(offset from sidereal) causes RA drift - Guide rates are in degrees per sidereal second (RA) or degrees per SI second (Dec)
Domain Types
All ASCOM enums serialize to their integer values via serde_repr:
use ;
use ;
use ShutterState;
use ;
use GuideDirection;
let state = Exposing; // serializes as 2
let side = East; // serializes as 0
Feature Flags
All device types are enabled by default. Disable defaults to reduce binary size on embedded:
[]
= { = false, = ["safety_monitor", "switch"] }
| Feature | Device Type | Methods |
|---|---|---|
camera |
Camera | ~55 |
cover_calibrator |
CoverCalibrator | 12 |
dome |
Dome | 22 |
filter_wheel |
FilterWheel | 7 |
focuser |
Focuser | 12 |
observing_conditions |
ObservingConditions | 15 |
rotator |
Rotator | 12 |
safety_monitor |
SafetyMonitor | 1 |
switch |
Switch | 16 |
telescope |
Telescope | ~60 |
all-devices |
All of the above | (default) |
conformu |
ConformU test harness | Adds tiny_http, dispatch layer, mock devices |
Integration Guide
Wiring to an HTTP Server
This crate provides protocol types only — you bring your own HTTP server. Here's the pattern:
use *;
use normalize_params;
// 1. Parse the URL: /api/v1/{device_type}/{device_number}/{method}
// 2. Extract query params + PUT body params, normalize keys to lowercase
// 3. Look up the device in the registry
// 4. Call the trait method
// 5. Build the response envelope
Management API
Every Alpaca server must expose three management endpoints:
use *;
// GET /management/apiversions → [1]
let versions = ApiVersions ;
// GET /management/v1/description → server info
let desc = ServerDescription ;
// GET /management/v1/configureddevices → device list
let devices = registry.configured_devices;
UDP Discovery
Alpaca clients discover servers via UDP broadcast on port 32227:
use *;
// Listen for "alpacadiscovery1" on port 32227
// Respond with: {"AlpacaPort": 32888}
let response = DiscoveryResponse ;
ESP32 / Embedded
The crate has no std networking dependency. For ESP32:
[]
= { = false, = ["safety_monitor"] }
Use with esp_http_server (C bindings) or any ESP-IDF HTTP server. The response types serialize to JSON via serde, which you send as the HTTP response body.
ConformU Validation
The conformu feature provides a complete test harness for validating your protocol implementation against the ASCOM ConformU conformance checker:
# Then run ConformU against http://127.0.0.1:32888
The harness includes:
- Mock implementations for all 10 device types with configurable capabilities
- Complete URL-to-trait dispatch layer (
dispatch_request) - Management API handlers
- All 11 mock devices pass ConformU with 0 errors, 0 issues
Using the Dispatch Layer
The conformu dispatch module provides a ready-made URL router:
use ;
if let Some = parse_device_path
Mock Devices for Testing
Each mock is configurable. Example with Camera feature flags:
use ;
// Minimal camera
let cam = new;
// Full-featured with numeric gain
let cam = full_featured;
// Color camera with named gain/offset
let cam = with_features;
Comparison with ascom-alpaca
ascom-alpaca-core |
ascom-alpaca |
|
|---|---|---|
| Protocol types | Yes | Yes |
| HTTP server | No (bring your own) | Built-in (axum) |
| Async runtime | None required | tokio required |
| ESP32 compatible | Yes | No |
| Binary size impact | Minimal (~types only) | Large (pulls in tokio, hyper, axum) |
| ConformU harness | Optional feature | No |
Use ascom-alpaca-core when:
- You need ESP32 or embedded support
- You have your own HTTP server (esp-idf, actix, warp, etc.)
- You want minimal dependencies
- You need the dispatch/mock layer for testing
Use ascom-alpaca when:
- You want batteries-included client+server on desktop
- You're already using tokio/axum
MSRV
Rust 1.75 or later.
License
Licensed under the Apache License, Version 2.0 (LICENSE or http://www.apache.org/licenses/LICENSE-2.0).
Unless required by applicable law or agreed to in writing, software distributed under this license is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.