laser-dac
Unified DAC backend abstraction for laser projectors.
This crate provides a complete solution for communicating with various laser DAC hardware:
- Discovery: Automatically find connected DAC devices (USB and network)
- Workers: Background threads for non-blocking frame output
- Backends: Unified
DacBackendtrait for all DAC types
This crate does not apply any additional processing on points (like blanking), except to make it compatible with the target DAC.
⚠️ Warning: use at your own risk! Laser projectors can be dangerous.
Supported DACs
| DAC | Connection | Verified |
|---|---|---|
| Helios | USB | ✅ |
| Ether Dream | Network | ✅ |
| IDN (ILDA Digital Network) | Network | ✅ |
| LaserCube WiFi | Network | ❌ |
| LaserCube USB / Laserdock | USB | ✅ |
The DACs that are not verified, I have not tested with the DAC itself yet. Help to test these would be very welcome!
IDN a standardized protocol. So any DAC that supports IDN would be supported. The implementation was tested with the HeliosPRO.
Quick Start
Connect your laser DAC and run an example. For full API details, see the documentation.
# or:
# callback mode (DAC-driven timing):
The examples run continuously until you press Ctrl+C.
Discovery Behavior
There are two discovery APIs:
DacDiscoveryWorkercontinuously scans in a background thread and auto-connects to new devices (predicate optional), yielding ready-to-useDacWorkerinstances.DacDiscoveryis manual: you callscan()and decide if/when toconnect()eachDiscoveredDevice.
DacDiscoveryWorker runs a background thread that:
- Scans periodically (default: every 2 seconds, configurable via
discovery_interval()) for new devices across all enabled DAC types - Reports all discovered devices via
poll_discovered_devices()regardless of filter - Auto-connects to filtered devices and yields ready-to-use workers via
poll_new_workers() - Automatic reconnection - when a device connection fails, it's removed from tracking and will reconnect on the next scan
Multiple devices of the same type are supported - each is identified by a unique name (MAC address for Ether Dream, serial number for LaserCube, etc.)
You can provide a device filter predicate to control which devices are auto-connected:
use ;
use Duration;
let discovery = builder
.enabled_types
.device_filter
.discovery_interval
.build;
// All discovered devices are visible, even those not matching the filter
for device in discovery.poll_discovered_devices
// Only filtered devices become workers
for worker in discovery.poll_new_workers
Streaming Modes
There are two ways to stream frames to a DAC:
Push Mode (DacWorker)
You control timing by calling submit_frame() in your main loop. Simple and works well for most cases:
use ;
let discovery = builder
.enabled_types
.build;
// Get workers from discovery...
let mut workers: = discovery.poll_new_workers.collect;
loop
Callback Mode (DacCallbackWorker)
The DAC drives timing by invoking your callback whenever it's ready for more data. This is ideal when you want the DAC to control the frame rate, or when generating frames on-demand:
use ;
use DacDiscovery;
let mut discovery = new;
for device in discovery.scan
The callback receives a context with frames_written count. Return Some(frame) to send data, or None to signal completion.
Coordinate System
All backends use normalized coordinates:
- X: -1.0 (left) to 1.0 (right)
- Y: -1.0 (bottom) to 1.0 (top)
- Colors: 0-65535 for R, G, B, and intensity
Each backend handles conversion to its native format internally.
Data Types
| Type | Description |
|---|---|
LaserFrame |
Collection of points + PPS rate |
LaserPoint |
Single point with position (f32) and color (u16) |
DacType |
Enum of supported DAC hardware |
DacDevice |
Device name + type |
DacConnectionState |
Connected or Lost state |
Features
By default, all DAC protocols are enabled via the all-dacs feature.
DAC Features
| Feature | Description |
|---|---|
all-dacs |
Enable all DAC protocols (default) |
usb-dacs |
Enable USB DACs: helios, lasercube-usb |
network-dacs |
Enable network DACs: ether-dream, idn, lasercube-wifi |
helios |
Helios USB DAC |
lasercube-usb |
LaserCube USB (LaserDock) DAC |
ether-dream |
Ether Dream network DAC |
idn |
ILDA Digital Network DAC |
lasercube-wifi |
LaserCube WiFi DAC |
For example, to enable only network DACs:
[]
= { = "*", = false, = ["network-dacs"] }
Other Features
| Feature | Description |
|---|---|
serde |
Enable serde serialization for DacType and EnabledDacTypes |
USB DAC Requirements
USB DACs (helios, lasercube-usb) use rusb which requires CMake to build.
Acknowledgements
- Helios DAC: heavily inspired from helios-dac
- Ether Dream DAC: heavily inspired from ether-dream
- Lasercube USB / WIFI: inspired from ildagen (ported from C++ to Rust)
- IDN: inspired from helios_dac (ported from C++ to Rust)
License
MIT