Skip Navigation Linkswww.mathertel.de --> Arduino Projects --> DMXSerial DMX Library

A Arduino Library for sending and receiving DMX

This is a library for sending and receiving DMX codes using the Arduino plattform or a ATmega (ATmega168, ATmega328 or similar) processor with a clock speed of 16 MHz.

The library avoids timming issues on the low level protocol that typically exist when using software implementations by using the internal serial communication hardware of the chips. Therefore it is very uncritical to add you specific DMX effects on top of this communication library.

DMX sending and DMX receiving are both supported by this library. You can find a brief description of using the details of the serial hardware registers in this article.

Download

Download the library files including 3 samples for sending and receiving DMX messages:

or use the latest version from github:

Hardware

This sketch and library shows how to use the hardware serial interface for sending and receiving DMX data packages.

To have a minimal DMX compatible hardware you have to add a RS-485 driver chip like the MAX481 and attach it to the hardware based serial interface of the ATMEGA Microcontroller.  A corresponding Arduino compatible DMX Shield can be found at DMXShield.aspx.

DMX and RDM

DMX521 or DMX in short was defined to control stage lightning effects by using XLR style cables. A good starting point for more information about the history and evolution of DMX can be found on Wikipedia: http://en.wikipedia.org/wiki/DMX512

Some Arduino related information is also available on the playground of the Arduino WebSite: http://www.arduino.cc/playground/Learning/DMX

While DMX was designed to have a single sender and multiple receivers. In 2006 a bi-directional protocol called RDM was defined that enhances DMX that enables a bi-directional communication that I like to support as well. See also: http://en.wikipedia.org/wiki/RDM_(lighting)

Both, the hardware and software decisions and designs were made to support RDM and DMX and die Arduino Shield can be used for all of it.

With this first version however only DMX Controllers (only sending) and DMX Receivers (only listening) are supported mode of operations.

Before starting the soldering and programming I did some research on existing DMX interface projects and checked for good and bad design hints.

DMX protocol levels

The logical level of DMX communication is built upon serial sending with 250.000 baud according the RS-485 definition using no parity bits and 2 stop bits.

DMX serial level schema

Luckily the hardware serial ports built in the ATMEGA chips used on the Arduino board supports this kind of communication and this high baud rate. see also http://en.wikipedia.org/wiki/RS-485

There is nothing really special about the levels of this communication protocol except the reset frame which is low for more than 88 µsec. The 2 stop bits have a minimum time of 8 µsecs but this time can be longer up to one second.

After a reset frame the payload bytes are transmitted by the DMX host using a start byte (value 0) and maximum of 512 channel value bytes. It is not recommended to send less than 24 channels.

This DMX and RDM specification allows sending and receiving DMX by using the built-in hardware. The tricks we need will be explained further down.

Don't use the Arduino Serial implementation

I have learned to avoid using the standard Serial implementation of the Arduino. There is a problem with it when accessing the hardware base serial interface because the serial kind of parity and stop bit handling is not set at all and the standard values are used.

Instead of using the built-in Serial object that is implemented in the HardwareSerial library this library initializes and controls all the Control Registers by itself.

There was a first approach published on http://arduino.cc/playground/Learning/DMXSerial  in 2011 that used the build-in Serial implementation but it turned out that when using the Interrupt features there is a implementation conflict with the interrupt routines needed for DMX and the existing interrupt routines of the standard Serial implementation. The http://arduino.cc/playground/DMX/Ardmx site mentions that the core file has to be modified - but this is not true in Serial is not used at all.

So you have to avoid using the Serial object at all and even don't use it for debugging purpose!

Using the serial interface through registers

There are several hardware settings that have to be implemented to enable the built-in serial interface. Her I just go through the implementation for your orientation how it's done.

Sending and receiving DMX The logical level of DMX communication is built upon serial sending up to 512 bytes by using 250.000 baud timings no parity bits and 2 stop bits. Luckily the hardware serial ports built in the ATMEGA chips used on the Arduino board supports this kind of communication and this high baud rate.

Enabling the hardware has to be done by setting the corresponding bits in the USART Control and Status Register B. The following does enabling sending and the corresponding Data Register Empty Interrupt in the case of the DMXController mode:

// Enable transmitter and interrupt
UCSRnB = (1<<TXENn) | (1<<UDRIEn);

Enabling the receiver and the corresponding Receive Complete Interrupt in the case of the DMXReceiver mode is very similar:

// Enable receiver and Receive interrupt
UCSRnB = (1<<RXENn) | (1<<RXCEn);

Setting the baud rate is implemented in the function _DMXSerialBaud because we often have to change it when sending DMX packages. The source code of this function is very similar to the one used in the standard Serial implementation, except that it also sets the transmission mode to 2 stop bits, 8 data bits and no parity:

// initialize the Hardware serial port with the given baud rate
// using 8 data data bits an 2 stop bits.
void _DMXSerialBaud(long baud)
{
  uint16_t baud_setting;

  // calculate baud settings
  baud_setting = (F_CPU / 8 / baud - 1) / 2;

  // assign the baud_setting to the USART Baud Rate Register
  UBRRnH = baud_setting >> 8;
  UBRRnL = baud_setting;

  // 2 stop bits and 8 bit character size, no parity
  UCSRnC = (1<<USBSn) | (3<<USCZn0);
} // _DMXSerialBaud

Sending a byte using the hardware is done by writing a new value into the USART Data Register after checking that there is no current transmission.

void _DMXSerialWrite(uint8_t data)
{
  // Wait for empty transmit buffer
  while ( ! (UCSRnA & (1 << UDREn)) )
    ;
  // Put data into buffer, sends the data
  UDRn = data;
} // _DMXSerialWrite

Where you can find details about this line of code

In the official documentation from ATMEL for the ATmega328P microcontroller there is a chapter 20 about the internal Universal Synchronous and Asynchronous serial Receiver and Transmitter (USART) starting on page 178. There are also microcontroller chips in the ATmega family that have more than one USART, so the USART also is referenced by the USART with number 0 (USART0).

In the block diagram you can see that there are some software addressable registers (in bold) used to configure these ports. The name of the registers are UBRRn, UDRn, UCSRnA, UCSRnB and UCSRnC where n is the placeholder for the number of the USART. The concrete registers for the USART0 are:

UBRR0H and UBRR0L: Baud Rate Registers

UDRn: I/O Data Registers

UCSR0A: Control and Status Register A

UCSR0B: Control and Status Register B

UCSR0C: Control and Status Register C

In chapter 20.11 Register Description you can find a short description of each register does.

The Arduino Serial library takes care of many configuration settings when initialized by calling Serial.begin([baud rate]). In the folder \hardware\arduino\cores\arduino you can find the file HardwareSerial.cpp with the implementation of this library. You can search for the begin method and can see that the baud rate is used to calculate the values for the registers UBRR0H (_ubrrh) and UBRR0L (_ubrrl) and some bits of the register UCSR0B (_ucsrb) are set. There is no initialization of the Control and Status Register C at all.

This line of code sets the serial communication mode to asynchronous mode (UMSEL01 and UMSEL00 are not set), no parity mode (UPM01 and UPM02 are not set), 2 stop bits (1<<USBS0) and 8 bit character size (3<<UCSZ00).

The Receive interrupt routine

Receiving a byte is not implemented using a function, instead a interrupt service routine is registered and will be called when a data package was detected and received by the hardware. No CPU cycles are needed for waiting. The received data is available in the USART Data Receive Register UDRn and a potential buffer overflow that will happen when receiving the DMX reset signal is available in USART Control State Register A UCSRnA. Both values are stored to local variables for later use:

ISR(USART_RX_vect) {
uint8_t USARTstate= UCSRnA; //get state before data!
uint8_t DmxByte = UDRn; //get data
...
}

The Sending interrupt routine

This interrupt routine is called by the hardware just after a byte was completely sent. This routine will then check for the next byte that has to be sent out. In DMX Controller mode the routine starts again with sending the reset signal when all data of the complete package is sent.

ISR(USART_UDRE_vect) { ... }

Sending the reset

The standard Arduino chip supports the 250.000 baud transfer rate that is used by DMX. The only exception to a plain 250.000 baud communication this is the BREAK / reset signal that is a physical low level on the wire for more than 88 µsec.

This 88 µsec low signal cannot be sent by using the 250.000 baud rate. For sending the break a slower baud rate is initialized, a 0-byte is sent and the baud rate is reset to the correct 250.000 baud.

// setup a slower baud rate
_DMXSerialBaud(115200);
// and send a 0 byte
_DMXSerialWrite((uint8_t)0);

After the BREAK has been sent the following code switches back to 250.000 baud for sending the data bytes:

Serial.begin(250000); 
Serial.write((uint8_t)0);

The second problem with the format used by DMX is the number of stop bits. Normally when using the serial port only one stop bit is used. Adding a second stop bit cannot be done through a configuration parameter or library call but must be done by adjusting the hardware setting for the serial port 0:

UCSR0C = (1<<USBS0)|(3<<UCSZ00);

You can find this code in the interrupt service routine to start a complete DMX package and continuously sending out the DMX values in the DMXController mode.

A design for a DMX Shield that works with this library can be found in the article: DMX Shield.

How to detect, that a device is disconnected?

It is not possible to detect that no cable is actually attached to a device without adding more hardware. So there is a more soft (software based) solution for that.

When a DMX start packet is received the library remembers the current time to a static variable. By using this value the method "DMXSerial.noDataSince()" the returns the number of milliseconds since the last packet was received.

The time is calculated by using the millis() function.

In the main loop or anywhere else you can check if the DMX signal was received during the last time and react if it is too long ago.

In the DmxSerialRecv.pde sample there is a call to the function for showing a fallback color (red in my case).

Links

DMX on the Arduino website. A good starting point for research:
http://www.arduino.cc/playground/Learning/DMX

About the HSL HSV and RGB Color models:
http://en.wikipedia.org/wiki/HSL_and_HSV

The documentation for the ATmega328P microcontroller can be found at:
http://www.atmel.com/dyn/resources/prod_documents/doc8271.pdf

RS-485 description
http://en.wikipedia.org/wiki/RS-485

DMX description
http://en.wikipedia.org/wiki/DMX512

MAX481 and MAX485 from MAXIM: http://www.maxim-ic.com/datasheet/index.mvp/id/1111

History

  • 24.08.2011 First published version.
  • 04.12.2011 Updated for compatibility with the Arduino 1.0 environment.
  • 10.05.2012 Added the method DMXSerial.noDataSince()
  • 04.06.2012 Now initializing the register UCSRnA to 0 correctly to avoid problems with Arduino Unos double speed baud settings.
  • 11.08.2012 Update to adjust some timing conditions and optimizations. Thanks a lot to Jonathan L.
  • 19.05.2013 Update to support multiple boards and configurations: MEGA 2560, Leonardo etc.
  • 19.05.2013 Update to support ATmega8 chips (still beta).
  • 01.07.2013 Added another example sketch.
  • 26.08.2013 Some minor fixes.

This page is part of the http://www.mathertel.de/ web site.