Send DMX Signal to control RGB-LED via Adafruit TLC5947

I am working on a solution to control multiple RGB-LED (via a PWM-Driver TLC5947 from Adafruit) from a software on my computer, that sends out DMX signal (Node Management Utility). The software is very basic, it has 512 channels that can fade from 0 to 255. The DMX signal is sent via ethernet cable to an EthernetShield that is attached to my Arduino Uno.

I downloaded the TLC5947 library and run the test programm. Everything worked just fine. Then I tried to receive Art-Net packets (the TLC5947 Driver was disconnected at the time). I got the different values on the console, when I played with the first fader from the software.

I am running into a problem, when i try to combine these two objects. I initialise the PWM Driver in the void setup() in the Art-Net-Receiving-Sketch:

void setup() {
  Serial.begin(9600);
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);
  
  // PWM Driver initialisation 
  PWM_DRIVER.begin(); //this somehow causes trouble with the code...
  if (OE >= 0) {
    pinMode(OE, OUTPUT);
    digitalWrite(OE, LOW);
  }
}

But then, somehow the void loop() is playing crazy! The loop seems to run very slowly, I can't fade properly anymore. Sometimes it even seems that the loop is stucked. :o Though I haven't even sent an order to the PWM Driver... The only thing I did is trying to set it up. When I outcomment PWM_DRIVER.begin(); , it works again.

What could be the reason for this clash? Thanks for any help!

full code:

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008
#include "Adafruit_TLC5947.h"

#define short_get_high_byte(x) ((HIGH_BYTE & x) >> 8)
#define short_get_low_byte(x)  (LOW_BYTE & x)
#define bytes_to_short(h,l) ( ((h << 8) & 0xff00) | (l & 0x00FF) );

// ADAFRUIT TLC5947 settings
#define NR_OF_DRIVERS 1
#define DATA   4
#define CLOCK   5
#define LATCH   6
#define OE  -1  // set to -1 to not use the enable pin (its optional)

Adafruit_TLC5947 PWM_DRIVER = Adafruit_TLC5947(NR_OF_DRIVERS, CLOCK, DATA, LATCH);

// network settings
byte mac[] = {0xA8, 0x61, 0x0A, 0xAE, 0x51, 0x3D} ; 
byte ip[] = {169, 254, 170, 13}; 
byte remoteIp[4];
unsigned int remotePort;

byte SubnetID = {0};
byte UniverseID = {0};
short select_universe = ((SubnetID*16)+UniverseID);

// Art-Net receiving "settings"
const int NR_OF_CHANNELS = 4;
const int START_ADDRESS = 0;

const int MAX_BUFFER_UDP = 768;
char packet_buffer[MAX_BUFFER_UDP]; // buffer to store incoming data
byte buffer_channel_arduino[NR_OF_CHANNELS];

unsigned int localPort = 6454; // artnet UDP port is by default 6454
const int art_net_header_size = 17;
const int max_packet_size = 576;
char ArtNetHead[8] = "Art-Net";
char OpHbyteReceive = 0;
char OpLbyteReceive = 0;
short incoming_universe = 0;
boolean is_opcode_is_dmx = 0;
boolean is_opcode_is_artpoll = 0;
boolean match_artnet = 1;
short opcode = 0;
EthernetUDP Udp;

void setup() {
  Serial.begin(9600);
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);
  
  // PWM Driver initialisation 
  PWM_DRIVER.begin(); //this somehow causes trouble with the code...
  if (OE >= 0) {
    pinMode(OE, OUTPUT);
    digitalWrite(OE, LOW);
  }
}

void loop() {
  
  int packet_size = Udp.parsePacket();

  if(packet_size > art_net_header_size && packet_size <= max_packet_size) {
        
    IPAddress remote = Udp.remoteIP();    
    remotePort = Udp.remotePort();
    Udp.read(packet_buffer, MAX_BUFFER_UDP);
    
    //read header
    match_artnet = 1;
    for (int i = 0; i<7; i++) {
      //if not corresponding, this is not an artnet packet, so we stop reading
      
      if (char(packet_buffer[i]) != ArtNetHead[i]) {
        match_artnet = 0;
        break;
      } 
    } 
       
     //if it is an artnet header
    if(match_artnet == 1) {
      //operator code enables to know which type of message Art-Net it is
      opcode = bytes_to_short(packet_buffer[9], packet_buffer[8]);
       
      //if opcode is DMX type
      if(opcode == 0x5000) {
        is_opcode_is_dmx = 1;
        is_opcode_is_artpoll = 0;
      }   
       
      //if opcode is artpoll 
      else if(opcode == 0x2000) {
         is_opcode_is_artpoll = 1;
         is_opcode_is_dmx = 0;
      } 
       
      //if its DMX data we will read it now
      if(is_opcode_is_dmx = 1) {
         //read incoming universe
         incoming_universe = bytes_to_short(packet_buffer[15],packet_buffer[14])
         //if it is selected universe DMX will be read
         if(incoming_universe == select_universe) {
       
          //getting data from a channel position, on a precise amount of channels, this to avoid to much operation if you need only 4 channels for example
          //channel position
          for(int i=START_ADDRESS;i< NR_OF_CHANNELS;i++) {
            buffer_channel_arduino[i-START_ADDRESS]= byte(packet_buffer[i+art_net_header_size+1]);
          }
         }
      }
    }
    // here we get the values from 0 to 255 on our console when we use the first fader from our software that sends out dmx signal
    Serial.print(buffer_channel_arduino[0]);
    Serial.println();
  }  
}

When I outcomment PWM_DRIVER.begin(); , it works again.

What could be the reason for this clash

One of the other libraries you use wants to access the same timer as the PWM library.

thank you for the quick answer!

is it the SPI or the Ethernet library? how do I fix this?
I don't really see/understand what timer the pwm library (see code below) uses...

Adafruit_TLC5947.h

#ifndef _ADAFRUIT_TLC5947_H
#define _ADAFRUIT_TLC5947_H

#include <Arduino.h>

/*!
 *    @brief  Class that stores state and functions for interacting with
 *            TLC5947 24-channel PWM/LED driver
 */
class Adafruit_TLC5947 {
public:
  Adafruit_TLC5947(uint16_t n, uint8_t c, uint8_t d, uint8_t l);

  boolean begin(void);

  void setPWM(uint16_t chan, uint16_t pwm);
  void setLED(uint16_t lednum, uint16_t r, uint16_t g, uint16_t b);
  void write();

private:
  uint16_t *pwmbuffer;

  uint16_t numdrivers;
  uint8_t _clk, _dat, _lat;
};

#endif

Adafruit_TLC5947.cpp

#include <Adafruit_TLC5947.h>

/*!
 *    @brief  Instantiates a new TLC5947 class
 *    @param  n
 *            num of drivers (boards)
 *    @param  c
 *            Arduino pin connected to TLC5947 clock pin
 *    @param  d
 *            Arduino pin connected to TLC5947 data pin
 *    @param  l
 *            Arduino pin connected to TLC5947 latch pin
 */
Adafruit_TLC5947::Adafruit_TLC5947(uint16_t n, uint8_t c, uint8_t d,
                                   uint8_t l) {
  numdrivers = n;
  _clk = c;
  _dat = d;
  _lat = l;

  pwmbuffer = (uint16_t *)malloc(2 * 24 * n);
  memset(pwmbuffer, 0, 2 * 24 * n);
}


/*!
 *    @brief  Writes PWM data to the all connected TLC5947 boards
 */
void Adafruit_TLC5947::write() {
  digitalWrite(_lat, LOW);
  // 24 channels per TLC5974
  for (int16_t c = 24 * numdrivers - 1; c >= 0; c--) {
    // 12 bits per channel, send MSB first
    for (int8_t b = 11; b >= 0; b--) {
      digitalWrite(_clk, LOW);

      if (pwmbuffer[c] & (1 << b))
        digitalWrite(_dat, HIGH);
      else
        digitalWrite(_dat, LOW);

      digitalWrite(_clk, HIGH);
    }
  }
  digitalWrite(_clk, LOW);

  digitalWrite(_lat, HIGH);
  digitalWrite(_lat, LOW);
}

/*!
 *    @brief  Set the PWM channel / value
 *    @param  chan
 *            channel number ([0 - 23] on each board, so chanel 2 for second board will be 25)
 *    @param  pwm
 *            pwm value [0-4095]
 */
void Adafruit_TLC5947::setPWM(uint16_t chan, uint16_t pwm) {
  if (pwm > 4095)
    pwm = 4095;
  if (chan > 24 * numdrivers)
    return;
  pwmbuffer[chan] = pwm;
}

/*!
 *    @brief  Set LED
 *    @param  lednum
 *            led number
 *    @param  r
 *            red value [0-255]
 *    @param  g
 *            green value [0-255]
 *    @param  b
 *            blue value [0-255]
 */
void Adafruit_TLC5947::setLED(uint16_t lednum, uint16_t r, uint16_t g,
                              uint16_t b) {
  setPWM(lednum * 3, r);
  setPWM(lednum * 3 + 1, g);
  setPWM(lednum * 3 + 2, b);
}

/*!
 *    @brief  Setups the HW
 *    @return True if initialization was successful, otherwise false.
 */
boolean Adafruit_TLC5947::begin() {
  //if (!pwmbuffer)
    return false;

  pinMode(_clk, OUTPUT);
  pinMode(_dat, OUTPUT);
  pinMode(_lat, OUTPUT);
  digitalWrite(_lat, LOW);

  return true;
}

is it the SPI or the Ethernet library?

I don’t know you will have to look at the code in the library to see.

how do I fix this?

You have to rewrite the library to use another timer. Maybe this is too tricky for you. You could try not using a library but writing your own code to drive the PWM.

This sort of thing is a problem with the system of using libraries without actually understanding what they do.

The other solution would be to do the PWM function in hardware, Adafruit do a servo board that will work with LEDs as well.