TeensyDMX
A full-featured DMX library for Teensy 3, Teensy LC, and Teensy 4. "Programmable DMX and arbitrary USB serial device emulation."
Install / Use
/learn @ssilverman/TeensyDMXREADME
<a href="https://www.buymeacoffee.com/ssilverman" title="Donate to this project using Buy Me A Coffee"><img src="https://img.shields.io/badge/buy%20me%20a%20coffee-donate-orange.svg?logo=buy-me-a-coffee&logoColor=FFDD00" alt="Buy Me A Coffee donate button"></a>
Readme for TeensyDMX v4.3.0-snapshot
This is a full-featured library for receiving and transmitting DMX on Teensy 3, Teensy LC, and Teensy 4. It follows the ANSI E1.11 DMX512-A specification.
Table of contents
- Features
- The TODO list
- How to use
- DMX receive
- DMX transmit
- Technical notes
- Code style
- References
- Acknowledgements
Features
Some notable features of this library:
- Teensy's default serial buffer isn't used; the data goes directly to/from the DMX buffers from/to the UART ISRs. In other words, the library is asynchronous and runs independently; all you need to worry about is setting and getting channel data.
- Simple API: After setup, there's only two read calls (
readPacketandget) and two forms of one write call (setfor single and multiple channels). - The library properly handles DMX packets containing less than 513 slots.
- The transmitter refresh rate can be changed to something less than "maximum rate".
- The transmitter can be paused and resumed to allow for packets that must be adjacent to other packets. In other words, the asynchronous transmitter can be used synchronously. For example, System Information Packets (SIP) require this. See Annex D5 of ANSI E1.11.
- The transmitter timing parameters can be specified: BREAK, MAB, inter-slot MARK time, and MBB.
- The receiver checks for timeouts according to the DMX specification. It knows of the concept of being disconnected from a DMX transmitter when timeouts or bad BREAKs are encountered in the data stream.
- Packet and error statistics are available. These can be used to detect protocol problems, including timeouts, framing errors and bad BREAKs, short packets (those less than 1196us), and long packets (those that exceed 513 bytes).
- The receiver can be used synchronously through the use of the
ResponderAPI. Alternate start codes can not only be handled, for example, for Text Packets or System Information Packets (SIP), but responses can be sent back to the transmitter, for example for RDM. - Functions for handling 16-bit data.
Receiver timing limitations and RX line monitoring
When the RX line is not being monitored, there are limitations in the handling of received DMX frame timing. For BREAK and Mark after BREAK (MAB) times, only the following cases are checked and not accepted as a valid DMX frame start:
- BREAK duration < ~44us.
- BREAK duration + MAB duration < ~96us.
- BREAK < ~88us and MAB ≥ ~44us.
The following case is accepted as a valid frame start, even though it isn't compliant with the DMX specification:
- BREAK duration > ~52us and MAB duration < ~44us.
For example, if a BREAK comes in having a duration of 53us and then a MAB comes in having a duration of 43us, their sum is 96us, and so the packet will be accepted. More generally, this will allow BREAKs that are shorter than the minimum required 88us if the MAB is shorter than 44us.
This limitation does not exist if the RX line is monitored. To monitor the line,
connect it to a digital I/O-capable pin and call setRXWatchPin with the pin
number. The pin cannot be the same as the RX pin.
Transmitter timing limitations
The transmitter uses a UART to control all the output. On the Teensy, the UART runs independently. This means that any specified timings won't necessarily be precise. They will often be accurate to within one or maybe two bit times. An effort has been made, however, to make sure that transmitted timings are at least as long as the requested timings.
For example, if a 100us MAB is requested, the actual MAB may be 102 or 103us. If a 40us inter-slot time is requested, the actual time may be 44us. And so on.
Additionally, certain timings will have a minimum length that can't be shortened, for example the MBB. This is simply due to code execution time interacting with the interrupt and UART subsystems. For example, on a Teensy LC, the minimum MBB is about 119us, even if the requested value is 0us.
The TODO list
These are either in the works or ideas for subsequent versions:
- Asynchronous responder data. Currently, the data is sent synchronously inside the UART ISR where responders process the packet.
- Better MAB transmit timing, perhaps by somehow synchronizing with the baud rate clock.
- Explore much more precise transmitter timings by not using the UART.
How to use
The classes you'll need are in the qindesign::teensydmx namespace: Receiver
and Sender.
All class documentation can be found in src/TeensyDMX.h.
Examples
Receiver examples:
BasicReceive: A basic receive exampleFlasher: Change the flash speed of the board LED based on DMX input
Sender examples:
BasicSend: A basic send exampleChaser: Chases values across all channelsSendADC: Sends the value from an ADC over a DMX channelSendTestPackets: Sends test packets (start code 55h)
Examples that show how to utilize synchronous and asynchronous transmission:
SIPSenderAsync: Sends SIP packets asynchronouslySIPSenderSync: Sends SIP packets synchronously
Examples that show how to use a synchronous packet handler in a receiver:
SIPHandler: Understands System Information Packets (SIP) (start code CFh)TextPacketHandler: Understands text packets (start codes 17h and 90h)
Transmitter timing examples:
RegenerateDMX: Regenerates received DMX onto a different serial port and with different timings
Other examples:
FastLEDController: Demonstrates DMX pixel output using FastLED
A more complex example showing how to behave as a DMX USB Pro Widget is
in USBProWidget.
Synchronous vs. asynchronous operation
Both transmission and reception operate asynchronously. This means that there's potentially a continuous stream of data being sent or received in the background.
The transmitter will keep sending the same data until it's changed externally
using one of the Sender::set functions. Similarly, Receiver::readPacket and
Receiver::get will return only the latest data; if more than a small amount of
time elapses between calls, then the data may have been changed more than once.
Both Sender and Receiver have a mode where they can operate synchronously.
With Sender, the stream can be paused and resumed, and packets inserted at
appropriate spots. Similarly, Receiver can send received packets as they
arrive to start code-specific instances of Responder.
DMX receive
Code example
First, create an object using one of the hardware serial ports, different from any serial ports being used for transmission:
namespace teensydmx = ::qindesign::teensydmx;
teensydmx::Receiver dmxRx{Serial1};
Before using the instance, start the serial port internals:
dmxRx.begin();
Using your own buffer whose length is at least len bytes, check for a packet
having arrived, and if it has, copy len values starting from channel
startChannel:
// For this example, assume buf is at least len bytes long
int read = dmxRx.readPacket(buf, startChannel, len);
read will contain the number of bytes read, -1 if no packet is available, or
zero if no values were read.
Note that channel zero contains the start code, a special value, usually zero, that occupies the first byte of the packet. The maximum DMX packet size is 513, but may be smaller, depending on the system.
Each call to `
