MAIN   ARTICLES   CAMS   PROJECTS   PICTURES   CODE   CONTACT 


DMX512 Generated directly by Raspberry Pi board


Example video

This video shows Raspberry Pi driving an RGB DMX light.


Description and Code

This code tries to generate a DMX512 data stream directly from the Raspberry Pi board itself.
You should consider this code expermintal, on a non over clocked Pi it seems pretty fast and stable. It is important to make sure /boot/config.txt options for over clocking or setting RAM or core frequency are not present. If you overclock the Pi this code will produce some bad packets when executing 'ps ax' at the shell or with some network operations. The default non overclocked config.txt with the addition of the line disable_pvt=1 added to it seems stable. As the Pi board comes under higher CPU load the frame rate will drop but the DMX data should remain valid.

www.jonshouse.co.uk/download/rpi_dmaio_dmx512.tar.gz

Disclaimer

At the moment this code does not play nicely with Pi video playback, and possibly some other Pi features.

Even X is slow and pretty unusable. This technique is cheap and useful if all you need a light controller or ethernet to DMX bridge, but it is probably not the best option.

The instructions here require some thought and care, if you break things or hurt yourself then I am in no way liable.



Some theory

One of the clever features of modern ARM chips is the ability to use DMA for Input Output (I/O) operations. DMA is timed by hardware and once started does not need intervention by the CPU, this has twin advantages. Being timed by the chip rather than by software means that the waveform generated should be pretty accurate, the second advantage is that as the software is not involved in actually clocking the bits the processor can be doing another job while this is happening.

Linux is not a real time operating system, unlike with a smaller microprocessor with no operating system, an AVR or PIC for example, it is not possible with Linux to guarantee timing when controlling outputs in software alone, the operating system will stop running your program (context switch) and will go and run some other tasks for a while before switching back to run more of your program. DMA I/O gets around this issue. Using DMA I/O does not magically make Linux real time, but in this application the jitter (difference in timing caused by the non real time scheduling) is hidden in the gap between packets rather than corrupting the packets themselves, the line is held high during this period so the jitter does not matter at all. The inter-packet time is not specified in the DMX protocol other than it must be under 1 second.


Some background

I started the project generating DMX512 from the Pi by thinking I would use the UART. I even wrote some code, the snag is that as default the Linux serial device driver/hardware is not setup for the high baud rate, you need to jump through some hoops. I could never quite make it work, however while searching around the net I found the pigpio library.

The pigpio library, written by someone much smarter than me, is able to take a table of output states (1s and 0s) and durations (a wave table) and setup DMA to generate this as an output. After reading some of the technical description I dropped the author an email to ask if they thought it was possible to generate a 250k bit serial data stream using the DMA I/O function. I received a very helpful email saying that it was and offering me useful information. Many thanks to the author and maintainer of pigpio, this project would not have been possible without their hard work and helpful emails with advice and solutions when I had some issues.


Hardware

DMX512 physical signal is based on differential signalling (RS485), a converter of some sort is needed to take the logic level signal from the Pi and turn it into power suitable for driving long cables. The hardware needed is very simple (and very cheap!). Search on ebay or an online shop for 'MAX485 module'. On ebay they are selling for about a UK pound as long as you are prepared to wait for it to arrive from China. This module has a MAX485 IC by Maxim on it, its an RS485 transceiver IC running from 5Volts. This chip converts differential signalling into single ended and vice versa.

RS485 module

A quick look at the MAX485 datasheet confirms the IC needs to run from 5v power (3.3v is not enough to properly power the chip). It will accept the 3.3v logic the Raspberry Pi generates. See VIH >2v in the Maxim datasheet.

The snag is that the Raspberry Pi (rpi) is not 5volt tolerant (it does not accept 5v inputs) but the module has pull-up resistors on the I/O lines pulling the logic signals up to 5v. These resistors need to be removed otherwise damage may result to the rpi, things may get hot, smell bad at best or fail completely at worst. I cant be more clear than this, failing to remove the 4 pullup resistors on the signals marked RO, RE, DE and DI may kill your rpi board. It is not enough to cut the power track running to the resistors, they must be completely removed instead.


Step by step Howto

Step1:

Organise the following parts and tools.

MAX485 board.
A socket or plug suitable for DMX, I am using a 3 pin XLR socket.
Cable, preferably red and black
Some 0.1 pin header cables, F/F. Search for 'dupont cable' on ebay.

Tools, soldering iron, screwdriver, brain.

Step 2:

Remove the 4 pull up resistors. These are normally next to the pins marked RO, RE, DE and DI

Step 3:

Wire board: The MAX485 board signal names are in blue

Step 4:

Wire the RS485 signal to a plug or socket. B is positive (red) and goes to pin3 on the socket. A is negative (black) and goes to pin 2. Image is socket viewed from back. At this point some electronics people think I am being dim, but the DMX spec is marked + and - and it is assumed it will idle with + on one side during its idle state (between packets).

Step 5:

Make sure Raspian and the firmware is up to date. edit /boot/config.txt and add the line 'disable_pvt=1' and re-boot the pi board.

If the kernel/firmware is not new enough and ignores this option then the DMX generated will have gaps in it, this causes the lights to flash from time to time. The pvt code in the Pi firmware checks the CPU temperature (and other things RAM related) from time to time, this seems to impact on DMA.

I am running this :
# /opt/vc/bin/vcgencmd version
May  1 2014 14:57:38 
Copyright (c) 2012 Broadcom
version f700bbe88d1c62de15ef87e00f360040a89a693e (clean) (release)
# uname -a
Linux raspberrypi 3.10.25+ #622 PREEMPT Fri Jan 3 18:41:00 GMT 2014 armv6l GNU/Linux

Step 6:

Login as root, if you dont have a root user use 'sudo bash' Download the rpidmx512.tar.gz software and unpack it.

Assuming your Pi is online
# wget www.jonshouse.co.uk/download/rpi_dmaio_dmx512.tar.gz
# gunzip rpi_dmaio_dmx512.tar.gz
# tar xvf rpi_dmaio_dmx512.tar

Now unpack the pigpio library, the version I used is shipped with my code. The latest version and documentation available from abyz.co.uk/rpi/pigpio/

# cd rpi_dmaio_dmx512
# unzip pigpio.zip
# cd PIGPIO
# make
# make install
# cd ..

Compile my code
# ./compile

Step 7:

Two programs are available 'rpi_dmaio_dmx512_cycle_test' and 'rpi_dmaio_dmx512_cycle_channels'. The channels one is the more useful.

rpi_dmaio_dmx512_cycle_test is written to be as simple as possible to show the technique for programmers, but as a utility it is pretty useless. It cycles all 512 DMX channels between values of 0 and 255, the snag is a lot of fixtures (dmx512 devices) have extra channels that are not lights, they have features or master faders for example. Setting all channels to the same value will probably give confusing results. This code is a good starting point if you want to write your own DMX code using my code as a base.

pi_dmaio_dmx512_cycle_channels is a utility that can set dmx channels to a fixed value or cycle dmx channels.

The syntax is

# ./pi_dmaio_dmx512_cycle_channels arguments are multiple CHANNEL=VALUE pairs. 

A special value of 400 is used to indicate a cycling value (starts and 0 and climbs to 255 then back down to zero).

Lets take a real light I have here as an example. It has 4 useful channels 1,2,3 and 4. Channel 1 is master fade, channels 2,3 and 4 are Red Green and Blue, Channel 5 is some strobe nonsense, channels 6 and 7 do some automated fade thing I have never used. To make this fixture work I need to set channel 1 to 255, then set and R,G,B value on channels 2,3 and 4 and ensure all other channels are 0.

I have given this fixture a DMX ID of 1

To cycle just green on this fixture I set master fade on channel 1 to 255 and cycle channel 2 :

# ./pi_dmaio_dmx512_cycle_channels 1=255 2=400

This will send all 512 DMX channels, most will be zero, except channel 1 that will always be 255 and channel 2 that will start as 0 and count up to 255, then count back down to zero.

To cycle red green and blue

# ./pi_dmaio_dmx512_cycle_channels 1=255 2=400 3=400 4=400
And so on ... This is all it does, but it is an example user mode driver rather than a useful application. With luck other people will include this code in their lighting systems/projects to allow more useful functionality.

Notes

I have my own lighting system, I am using a variation on this code to take ethernet DMX packets and bridge them onto physical DMX, this code uses 28% of the CPU to give 40Hz updates of all 512 channels per update. It is not open source (yet, maybe one day if I get the time). This code fragment may be useful though. It might be possible to use the RO line and sample from the bus, maybe get replies for fixtures - personally the idea leaves me cold but somebody might like to play with making that work.

int wave_id=-1;
send_dmx_packet(int numbits)
{
        int r=0;
        gpioWrite_Bits_0_31_Set( (1<<DE) );                                     // DE high (enable TX)

        if (wave_id<0)
        {
                r=gpioWaveAddGeneric(numbits, pulse);
                if (r!=numbits)
                {
                        printf("Error performing gpioWaveAddGeneric, it returned %d expected %d\n",r,numbits);
                        exit(1);
                }
                wave_id = gpioWaveCreate();
                if (gpioWaveTxSend(wave_id, PI_WAVE_MODE_ONE_SHOT)<0)           // Send this bit pattern once via DMA GPIO
                        printf("Warning, error performing gpioWaveTxSend()\n");
        }
        else
        {
                while (gpioWaveTxBusy())
                        usleep(20);
                gpioWaveDelete(wave_id);
                wave_id=-1;
        }
        usleep(1000);
}

More Notes

It seems this technique is a little limited, I guess its simpler to use a USB device and be done with it. I can however see a niche for the technique, other than just people like me being cheap with hardware. DMA can be used to clock bits out in parallel so an ethernet to multiple DMX bridge should be possible.

Update 2017: I must find the time to update the code for newer RPI models and re-test it.

The technique here seems only reliable on older kernels. To get best results use the Pi UART rather than the PIGPIO library to generate the DMX. The OLA project has drivers for Pi UART built in. OLA Raspberry Pi

Alisdair Robertson has done some good work here


All content on this site is (c) 2017 Jonathan Andrews, This article is marked as free and open. Feel free to copy and or republish it