dmx_rdm/lib.rs
1//! Rust library for communicating DMX512 (ANSI E1.11) and DMX-RDM (ANSI E1.20) over a RS485 bus by
2//! using interchangeable drivers. This library features no-std as well as no-alloc support
3//! (no heap allocation) to target embedded as well as os platforms.
4//!
5//! Please refer to the [official specifications](https://tsp.esta.org/) published by the ESTA.
6//!
7//! <div class="warning">This library is wip, it has not yet received extensive testing and the api
8//! might not be final.</div>
9//!
10//! # Usage
11//! These examples show the basic usage using the dmx-rdm-ftdi driver.
12//! These examples work together.
13//!
14//! ## Controller
15//!
16//! ```rust
17//! use dmx_rdm::dmx_controller::{DmxController, DmxControllerConfig};
18//! use dmx_rdm::unique_identifier::{PackageAddress, UniqueIdentifier};
19//! use dmx_rdm::utils::run_full_discovery;
20//! use dmx_rdm_ftdi::{FtdiDriver, FtdiDriverConfig};
21//!
22//! let dmx_driver = FtdiDriver::new(FtdiDriverConfig::default()).unwrap();
23//! let mut dmx_controller = DmxController::new(dmx_driver, &DmxControllerConfig::default());
24//!
25//! let mut devices_found = vec![];
26//!
27//! // Unmute all dmx responders.
28//! dmx_controller
29//! .rdm_disc_un_mute(PackageAddress::Broadcast)
30//! .unwrap();
31//!
32//! let mut uid_array = [UniqueIdentifier::new(1, 1).unwrap(); 512];
33//! loop {
34//! // Search for devices.
35//! let amount_devices_found = run_full_discovery(&mut dmx_controller, &mut uid_array).unwrap();
36//!
37//! // Add found devices to vector.
38//! devices_found.extend_from_slice(&uid_array[..amount_devices_found]);
39//!
40//! // Have all devices been found and muted?
41//! if amount_devices_found != uid_array.len() {
42//! break;
43//! }
44//! }
45//!
46//! for device in devices_found {
47//! match dmx_controller.rdm_set_identify(PackageAddress::Device(device), true) {
48//! Ok(_) => println!("Activated identify for device_uid {device}"),
49//! Err(error) => {
50//! println!("Activating identify for device_uid {device} failed with {error}")
51//! },
52//! }
53//! }
54//! ```
55//!
56//! ## Responder
57//!
58//! ```rust
59//! use dmx_rdm::command_class::RequestCommandClass;
60//! use dmx_rdm::dmx_receiver::{DmxResponderHandler, RdmResponder};
61//! use dmx_rdm::rdm_data::RdmRequestData;
62//! use dmx_rdm::rdm_responder::{DmxReceiverContext, RdmResponderConfig, RdmResult};
63//! use dmx_rdm::types::{DataPack, NackReason};
64//! use dmx_rdm::unique_identifier::UniqueIdentifier;
65//! use dmx_rdm_ftdi::{FtdiDriver, FtdiDriverConfig};
66//!
67//! struct RdmHandler {
68//! identify: bool,
69//! }
70//!
71//! const PID_IDENTIFY_DEVICE: u16 = 0x1000;
72//!
73//! impl RdmHandler {
74//! fn handle_get_identify(&self) -> RdmResult {
75//! RdmResult::Acknowledged(DataPack::from_slice(&[self.identify as u8]).unwrap())
76//! }
77//!
78//! fn handle_set_identify(&mut self, parameter_data: &[u8]) -> Result<RdmResult, std::fmt::Error> {
79//! // Check if the parameter data has the correct size
80//! if parameter_data.len() != 1 {
81//! return Ok(RdmResult::NotAcknowledged(
82//! NackReason::DataOutOfRange as u16,
83//! ));
84//! }
85//!
86//! // Convert identify flag to bool and set that in the state.
87//! self.identify = parameter_data[0] != 0;
88//!
89//! println!("Current identify is {}", self.identify);
90//!
91//! // Acknowledge request with an empty response.
92//! Ok(RdmResult::Acknowledged(DataPack::new()))
93//! }
94//! }
95//!
96//! impl DmxResponderHandler for RdmHandler {
97//! type Error = std::fmt::Error;
98//!
99//! fn handle_rdm(
100//! &mut self,
101//! request: &RdmRequestData,
102//! _: &mut DmxReceiverContext,
103//! ) -> Result<RdmResult, Self::Error> {
104//! match request.parameter_id {
105//! PID_IDENTIFY_DEVICE => match request.command_class {
106//! RequestCommandClass::GetCommand => Ok(self.handle_get_identify()),
107//! RequestCommandClass::SetCommand => {
108//! self.handle_set_identify(&request.parameter_data)
109//! },
110//! _ => Ok(RdmResult::NotAcknowledged(
111//! NackReason::UnsupportedCommandClass as u16,
112//! )),
113//! },
114//! _ => Ok(RdmResult::NotAcknowledged(NackReason::UnknownPid as u16)),
115//! }
116//! }
117//! }
118//!
119//!
120//! let dmx_driver = FtdiDriver::new(FtdiDriverConfig::default()).unwrap();
121//!
122//! // Create rdm_responder with space for 32 queued messages.
123//! let mut dmx_responder = RdmResponder::<_, 32>::new(
124//! dmx_driver,
125//! RdmResponderConfig {
126//! uid: UniqueIdentifier::new(0x7FF0, 1).unwrap(),
127//! // Won't add PID_IDENTIFY_DEVICE since this is a required pid.
128//! supported_pids: &[],
129//! rdm_receiver_metadata: Default::default(),
130//! },
131//! );
132//!
133//! let mut rdm_handler = RdmHandler { identify: false };
134//!
135//! loop {
136//! // poll for new packages using our handler
137//! match dmx_responder.poll(&mut rdm_handler) {
138//! Ok(_) => (),
139//! Err(error) => println!("'{error}' during polling"),
140//! }
141//! }
142//! ```
143//!
144
145#![cfg_attr(not(feature = "std"), no_std)]
146#![cfg_attr(docsrs, feature(doc_cfg))]
147
148pub mod command_class;
149pub mod consts;
150/// Module for building dmx-rdm controllers.
151pub mod dmx_controller;
152/// Module for implementing more sophisticated dmx-rdm devices like Enttec DMX Pro, as well as
153/// mockups, where a simple uart implementation won't suffice.
154pub mod dmx_driver;
155/// Module for building dmx-rdm receivers.
156pub mod dmx_receiver;
157/// Module for simplifying the implementation of new drivers/hardware that behave like direct uart devices.
158pub mod dmx_uart_driver;
159mod layouts;
160mod pids;
161pub mod rdm_data;
162pub mod rdm_packages;
163/// Parser for handling rdm requests without an underlying driver.
164/// Mainly for highly interrupt driven applications.
165pub mod rdm_responder;
166pub mod rdm_types;
167pub mod types;
168pub mod unique_identifier;
169pub mod utils;