SkillAgentSearch skills...

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/TeensyDMX

README

<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

  1. Features
    1. Receiver timing limitations and RX line monitoring
    2. Transmitter timing limitations
  2. The TODO list
  3. How to use
    1. Examples
    2. Synchronous vs. asynchronous operation
  4. DMX receive
    1. Code example
    2. Retrieving 16-bit values
    3. Error counts and disconnection
      1. The truth about connection detection
      2. Keeping short packets
    4. Packet statistics
    5. Error statistics
    6. Synchronous operation by using custom responders
      1. Responding
  5. DMX transmit
    1. Code example
    2. Packet size
    3. Transmission rate
    4. Synchronous operation by pausing and resuming
    5. Choosing BREAK and MAB times
      1. Specific BREAK/MAB times
        1. A note on BREAK timing
        2. A note on MAB timing
      2. BREAK/MAB times using serial parameters
    6. Inter-slot MARK time
    7. MBB time
    8. Error handling in the API
  6. Technical notes
    1. Simultaneous transmit and receive
    2. Transmission rate
    3. Transmit/receive enable pins
    4. Thread safety
    5. Dynamic memory allocation failures
    6. Hardware connection
    7. Receiver and driving the TX pin
    8. Potential PIT timer conflicts
  7. Code style
  8. References
  9. Acknowledgements

Features

Some notable features of this library:

  1. 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.
  2. Simple API: After setup, there's only two read calls (readPacket and get) and two forms of one write call (set for single and multiple channels).
  3. The library properly handles DMX packets containing less than 513 slots.
  4. The transmitter refresh rate can be changed to something less than "maximum rate".
  5. 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.
  6. The transmitter timing parameters can be specified: BREAK, MAB, inter-slot MARK time, and MBB.
  7. 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.
  8. 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).
  9. The receiver can be used synchronously through the use of the Responder API. 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.
  10. 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:

  1. BREAK duration < ~44us.
  2. BREAK duration + MAB duration < ~96us.
  3. 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:

  1. 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:

  1. Asynchronous responder data. Currently, the data is sent synchronously inside the UART ISR where responders process the packet.
  2. Better MAB transmit timing, perhaps by somehow synchronizing with the baud rate clock.
  3. 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 example
  • Flasher: Change the flash speed of the board LED based on DMX input

Sender examples:

  • BasicSend: A basic send example
  • Chaser: Chases values across all channels
  • SendADC: Sends the value from an ADC over a DMX channel
  • SendTestPackets: Sends test packets (start code 55h)

Examples that show how to utilize synchronous and asynchronous transmission:

  • SIPSenderAsync: Sends SIP packets asynchronously
  • SIPSenderSync: 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 `

View on GitHub
GitHub Stars106
CategoryDevelopment
Updated4d ago
Forks6

Languages

C++

Security Score

100/100

Audited on Mar 25, 2026

No findings