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)
- Streaming: Zero-allocation callback API with buffer-driven timing
- Backends: Unified interface 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 | Notes |
|---|---|---|---|
| Helios | USB | ✅ | |
| Ether Dream | Network | ✅ | |
| IDN (ILDA Digital Network) | Network | ✅ | IDN is a standardized protocol. We tested with HeliosPRO |
| LaserCube WiFi | Network | ✅ | Recommend to not use through WiFi mode; use LAN only |
| LaserCube USB / Laserdock | USB | ✅ |
All DACs have been manually verified to work.
Quick Start
Connect your laser DAC and run an example. For full API details, see the documentation.
The examples run continuously until you press Ctrl+C.
Streaming API
The streaming API uses buffer-driven timing: your callback is invoked when the buffer needs filling. This provides automatic backpressure handling and zero allocations in the hot path.
use ;
let devices = list_devices?;
let device = open_device?;
let config = new;
let = device.start_stream?;
stream.control.arm?;
let exit = stream.run?;
Return ChunkResult::Filled(n) to continue, ChunkResult::End to stop gracefully.
Reconnecting Session (optional)
If you want automatic reconnection by device ID, use ReconnectingSession:
use ;
use Duration;
let mut session = new
.with_max_retries
.with_backoff
.on_disconnect
.on_reconnect;
// Arm output as usual (this persists across reconnects)
session.control.arm?;
let exit = session.run?;
Note: ReconnectingSession uses open_device() internally, so it won't include
external discoverers registered on a custom DacDiscovery.
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 |
|---|---|
DacInfo |
DAC metadata (name, type, capabilities) |
Dac |
Opened DAC ready for streaming |
Stream |
Active streaming session |
ReconnectingSession |
Stream wrapper with automatic reconnect |
StreamConfig |
Stream settings (PPS, buffering, color delay, blanking) |
ChunkRequest |
Request info for filling point buffer |
LaserPoint |
Single point with position (f32) and color (u16) |
DacType |
Enum of supported DAC hardware |
Advanced Configuration
Color Delay (Scanner Sync Compensation)
Galvo mirrors need time to settle before the laser fires. color_delay shifts
RGB+intensity channels relative to XY coordinates so colors arrive after the
mirrors are in position.
use Duration;
let config = new
.with_color_delay;
Can also be changed at runtime via stream.control().set_color_delay(...).
Typical values: 50–200µs depending on scanner speed. Disabled by default.
Startup Blanking
Prevents the "flash on start" artifact by forcing the first points after arming to blank, giving mirrors time to reach their initial position.
use Duration;
let config = new
.with_startup_blank; // default: 1ms
Set to Duration::ZERO to disable.
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.
Development Tools
IDN Simulator
The repository includes a debug simulator (in tools/idn-simulator/) that acts as a virtual IDN laser DAC. This is useful for testing and development without physical hardware.
# Build and run the simulator
# With custom options
Features:
- Responds to IDN discovery (appears as a real DAC)
- Renders received laser frames as connected lines
- Handles blanking (intensity=0 creates gaps between shapes)
- Shows frame statistics (frame count, point count, client address)
Usage:
When the simulator is running, launch your work that scans for IDN devices. You can use this crate, or any other tool that supports IDN!
For a simple test, you can run one of our examples: cargo run --example stream -- circle
CLI Options:
| Option | Description | Default |
|---|---|---|
-n, --hostname |
Hostname in scan responses | IDN-Simulator |
-s, --service-name |
Service name in service map | Simulator Laser |
-p, --port |
UDP port to listen on | 7255 |
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