Another Arduino pixel controller

elnino

Full time elf
Joined
Aug 1, 2014
Messages
142
Hi all, I have finally been able to do some testing on my code and I am now willing to release it.


I have 2 versions - Functionally the same, just different hardware.


One is for the standard arduino ethernet shield, the other is for the ENC28J60 based module. The difference being that a Uno and Ethernet module will set you back about $20 whereas a Pro mini and ENC28J60 will cost about $6 but requires a bit more work to assemble etc.


This code will allow you to run up to 240 pixels - Possibly more but not a lot. The poor Atmega328 runs out of SRAM.


ENC28J60 Version:
Code:
// E1.31 Receiver and pixel controller
// This code may be freely distributed and used as you see fit for non-profit
// purposes and as long as the original author is credited and it remains open
// source
//
// The ethernet module is an ENC28J60 based item which are easily available
// for a few $
//
// Data connections to the module:
// Module -> Arduino
// SI     -> 11
// SO     -> 12
// SCK    -> 13
// CS     -> 8
//
// NB Most of the modules run on 3.3v, not 5v - Use caution!  5V logic/data is fine though.
//
// Please configure your Lighting product to use Unicast to the IP the device is given from your DHCP server
// Multicast is not currently supported due to bandwidth/processor limitations


// You will need the Ethercard and FastLed Libraries from:
// [url]https://github.com/FastLED/FastLED/releases[/url]
// [url]https://github.com/jcw/ethercard[/url]
//
// The Atmega328 only has 2k of SRAM.  This is a severe limitation to being able to control large
// numbers of smart pixels due to the pixel data needing to be stored in an array as well as
// a reserved buffer for receiving Ethernet packets.  This code will allow you to use a maximum of 240 pixels
// as that just about maxes out the SRAM on the Atmega328.


// There is deliberately no serial based reporting from the code to conserve SRAM.  There is a **little**
// bit available if you need to add some in for debugging but keep it to an absolute minimum for debugging
// only.




#include <EtherCard.h>
#include "FastLED.h"


//*********************************************************************************


// enter desired universe and subnet  (sACN first universe is 1)
#define DMX_SUBNET 0
#define DMX_UNIVERSE 1 //**Start** universe


// Set a different MAC address for each...
const byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x17 };


// Uncomment if you want to use static IP
//*******************************************************
// ethernet interface ip address
static byte myip[] = { 10,0,0,10 };
//*******************************************************


// By sacrificing some of the Ethernet receive buffer, we can allocate more to the LED array
// but this is **technically** slower because 2 packets must be processed for all 240 pixels.


/// DONT CHANGE unless you know the consequences...
#define ETHERNET_BUFFER 540
#define CHANNEL_COUNT 360 //because it divides by 3 nicely
#define NUM_LEDS 240 // can not go higher than this - Runs out of SRAM
#define UNIVERSE_COUNT 2
#define LEDS_PER_UNIVERSE 120




// The pin the data line is connected to for WS2812b
#define DATA_PIN 7


//********************************************************************************


// Define the array of leds
CRGB leds[NUM_LEDS];


byte Ethernet::buffer[ETHERNET_BUFFER]; // tcp/ip send and receive buffer


void setup() {
  // Using different LEDs or colour order? Change here...
  // ********************************************************
     FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS); 
  // ********************************************************
 


   // No checks in here to ensure Ethernet initiated properly.
   // Make sure Ethernet cable is connected and there is DHCP on the network ....
   ether.begin(sizeof(Ethernet::buffer), mymac);

// ******************************************************** 
   // DHCP
   //ether.dhcpSetup();
  
  //Static IP
  ether.staticSetup(myip);
// ********************************************************


  // Register listener
  ether.udpServerListenOnPort(&sACNPacket, 5568);


  //Once the Ethernet is initialised, run a test on the LEDs
  initTest();
}


void loop() {
   //Process packets
   ether.packetLoop(ether.packetReceive());
}


static void sACNPacket(word port, byte ip[4], const char *data, word len) {
  //Make sure the packet is an E1.31 packet
  int count = checkACNHeaders(data, len);
  if (count){
    // It is so process the data to the LEDS
    sacnDMXReceived(data, count);
  }
}


void sacnDMXReceived(const char* pbuff, int count) {
  if (count > CHANNEL_COUNT) count = CHANNEL_COUNT;
  byte b = pbuff[113]; //DMX Subnet
  if ( b == DMX_SUBNET) {
    b = pbuff[114];  //DMX Universe
    if ( b >= DMX_UNIVERSE && b <= DMX_UNIVERSE + UNIVERSE_COUNT ) { 
      if ( pbuff[125] == 0 ) {  //start code must be 0
      int ledNumber = (b - DMX_UNIVERSE) * LEDS_PER_UNIVERSE;
       // sACN packets come in seperate RGB but we have to set each led's RGB value together
       // this 'reads ahead' for all 3 colours before moving to the next led.
       //Serial.println("*");
       for (int i = 126;i < 126+count;i = i + 3){
          byte charValueR = pbuff[i];
          byte charValueG = pbuff[i+1];
          byte charValueB = pbuff[i+2];
          leds[ledNumber].setRGB(charValueR,charValueG,charValueB);
          ledNumber++;
        }
      FastLED.show();  //Do it!
      }
    }
  }
}


int checkACNHeaders(const char* messagein, int messagelength) {
  if ( messagein[1] == 0x10 && messagein[4] == 0x41 && messagein[12] == 0x37) {  
      int addresscount = (byte) messagein[123] * 256 + (byte) messagein[124]; // number of values plus start code
      return addresscount -1; //Return how many values are in the packet.
    }
  return 0;
}


void initTest() //runs at board boot to make sure pixels are working
{
  LEDS.showColor(CRGB(255, 0, 0)); //turn all pixels on red
   delay(1000);
   LEDS.showColor(CRGB(0, 255, 0)); //turn all pixels on green
   delay(1000);
   LEDS.showColor(CRGB(0, 0, 255)); //turn all pixels on blue
   delay(1000);
   LEDS.showColor(CRGB(0, 0, 0)); //turn all pixels off
}


Wiznet/Arduino Ethernet version:
Code:
// E1.31 Receiver and pixel controller
// This code may be freely distributed and used as you see fit for non-profit
// purposes and as long as the original author is credited and it remains open
// source
//
// Please configure your Lighting product to use Unicast to the IP the device is given from your DHCP server
// Multicast is not currently supported due to bandwidth/processor limitations


// You will need the Ethercard and FastLed Libraries from:
// [url]https://github.com/FastLED/FastLED/releases[/url]

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include "FastLED.h"


//*********************************************************************************


// enter desired universe and subnet  (sACN first universe is 1)
#define DMX_SUBNET 0
#define DMX_UNIVERSE 1 //**Start** universe


// Set a different MAC address for each...
byte mac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x15 };


// Uncomment if you want to use static IP
//*******************************************************
// ethernet interface ip address
IPAddress ip(10, 0, 0, 10);  //IP address of ethernet shield
//*******************************************************


EthernetUDP Udp;


// By sacrificing some of the Ethernet receive buffer, we can allocate more to the LED array
// but this is **technically** slower because 2 packets must be processed for all 240 pixels.


/// DONT CHANGE unless you know the consequences...
#define ETHERNET_BUFFER 540
#define CHANNEL_COUNT 360 //because it divides by 3 nicely
#define NUM_LEDS 240 // can not go higher than this - Runs out of SRAM
#define UNIVERSE_COUNT 2
#define LEDS_PER_UNIVERSE 120


// The pin the data line is connected to for WS2812b
#define DATA_PIN 7


//********************************************************************************


// Define the array of leds
CRGB leds[NUM_LEDS];


unsigned char packetBuffer[ETHERNET_BUFFER];


void setup() {
  // Using different LEDs or colour order? Change here...
  // ********************************************************
     FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS); 
  // ********************************************************



  // ******************************************************** 
  Ethernet.begin(mac,ip);
  Udp.begin(5568);
  // ********************************************************


  //Once the Ethernet is initialised, run a test on the LEDs
  initTest();
}


void loop() {
   //Process packets
   int packetSize = Udp.parsePacket(); //Read UDP packet count
   if(packetSize){
    Udp.read(packetBuffer,ETHERNET_BUFFER); //read UDP packet
    int count = checkACNHeaders(packetBuffer, packetSize);
    if (count) {
      sacnDMXReceived(packetBuffer, count); //process data function
    }
  }
}




void sacnDMXReceived(unsigned char* pbuff, int count) {
  if (count > CHANNEL_COUNT) count = CHANNEL_COUNT;
  byte b = pbuff[113]; //DMX Subnet
  if ( b == DMX_SUBNET) {
    b = pbuff[114];  //DMX Universe
    if ( b >= DMX_UNIVERSE && b <= DMX_UNIVERSE + UNIVERSE_COUNT ) { 
      if ( pbuff[125] == 0 ) {  //start code must be 0
      int ledNumber = (b - DMX_UNIVERSE) * LEDS_PER_UNIVERSE;
       // sACN packets come in seperate RGB but we have to set each led's RGB value together
       // this 'reads ahead' for all 3 colours before moving to the next led.
       //Serial.println("*");
       for (int i = 126;i < 126+count;i = i + 3){
          byte charValueR = pbuff[i];
          byte charValueG = pbuff[i+1];
          byte charValueB = pbuff[i+2];
          leds[ledNumber].setRGB(charValueR,charValueG,charValueB);
          ledNumber++;
        }
      FastLED.show();  //Do it!
      }
    }
  }
}


int checkACNHeaders(unsigned char* messagein, int messagelength) {
  if ( messagein[1] == 0x10 && messagein[4] == 0x41 && messagein[12] == 0x37) {  
      int addresscount = (byte) messagein[123] * 256 + (byte) messagein[124]; // number of values plus start code
      return addresscount -1; //Return how many values are in the packet.
    }
  return 0;
}


void initTest() //runs at board boot to make sure pixels are working
{
  LEDS.showColor(CRGB(255, 0, 0)); //turn all pixels on red
   delay(1000);
   LEDS.showColor(CRGB(0, 255, 0)); //turn all pixels on green
   delay(1000);
   LEDS.showColor(CRGB(0, 0, 255)); //turn all pixels on blue
   delay(1000);
   LEDS.showColor(CRGB(0, 0, 0)); //turn all pixels off
}

Enjoy!
 
Last edited:

JPB

Full time elf
Joined
May 13, 2010
Messages
352
Location
Glenwood
I loaded this into my Uno on the weekend with the ENC28J60 ethernet module and it is brilliant. Great work Elnino

Jon
 

damo1271

Full time elf
Joined
Oct 12, 2011
Messages
202
Location
Adelaide
I've tried this and cant seem to get it to work. Im using the ENC28J60 version

I changed the IP address to match the vixen IP address - 192.168.1.5
I changed the universe number to match the vixen setup universe 5
Vixen is set with universe 5 and universe 6 with 120 pixels each.

LEDs are connected to pin 7.

Other connections:
ENC Module > Arduino
SI > 11
SO > 12
SCK > 13
CS > 8

The Arduino sketch loads as I can see the test pattern at the startup.
I have power (3.3v) at the ENC as evidenced by the LED. There is no other LED activity at the ENC.

I can't seem to figure what I have done wrong but it seems to be related to the ethernet side of things :(
 

JPB

Full time elf
Joined
May 13, 2010
Messages
352
Location
Glenwood
The IP address is a unique address you choose for the board.
You then need to configure vixen to send the Universe you have specified to the UNICAST ip address you set the board as.

From a cmd window can you ping your arduino?
" Ping 192.168.1.5 "

What version vixen are you using?
 

damo1271

Full time elf
Joined
Oct 12, 2011
Messages
202
Location
Adelaide
Thanks. I've found the problem. :)
Works perfectly now.
It seems the Arduino was only outputting 2.0 volts when connected to the ENC. (It measured 3.3V under no load)
I swapped the ENC power to a dedicated 3.3V power module and it started working.

I am new to the Arduino so its a good lesson - Don't trust LEDs as a sign of "correct" power levels and don't assume the Arduino will output the correct voltage under all conditions!

Live and learn
 

damo1271

Full time elf
Joined
Oct 12, 2011
Messages
202
Location
Adelaide
JPB said:
From a cmd window can you ping your arduino?
" Ping 192.168.1.5 "

I originally couldn't ping the arduino. that's why I thought it might have been Ethernet related. I also wasn't sure if the ping was supported by the code. (should probably read the ENC28J60 datasheet one day!)
It now pings. So for anyone else with a problem, you can ping the arduino and it will respond.
 

damo1271

Full time elf
Joined
Oct 12, 2011
Messages
202
Location
Adelaide
Yes I have a few of those feelings in the last week or so.
I'm converting everything to vixen 3 and adding more pixel elements and controllers. I keep making stupid mistakes, but it is exciting when it all starts working.
 

elnino

Full time elf
Joined
Aug 1, 2014
Messages
142
damo1271 said:
It seems the Arduino was only outputting 2.0 volts when connected to the ENC. (It measured 3.3V under no load)
I swapped the ENC power to a dedicated 3.3V power module and it started working.


That is why I use the modules that have onboard 3.3v regulators. You'll notice these ones specifically have 5v and 3.3v in. i.e http://www.aliexpress.com/item/5pcs-lot-ENC28J60-Ethernet-LAN-Network-Module-Schematic-For-Arduino-51-AVR-LPC/851071899.html


It simplifies things a lot.
 

bob_moody

Apprentice elf
Joined
Oct 20, 2012
Messages
86
Location
Huntsville, TX
Elnino,
I know this is an older thread, but I wanted to thank you for the sketch. I have an Arduino Uno and just got in a RioRand Upgraded Ethernet Shield W5100.
The sketch compiled perfectly and I have been able to control a 40 count string of WS2811 modules without any issues. I have been able to use an OLD version of Xlights to test them and I was even able to control them via the LOR Sequencer.
I have been playing lately with different libraries as I just picked up several strings of the GECE pixels and dont yet have a SanDevice to run them.
I thought maybe I could modify your sketch to use the GECE lib that I have and utilize the Arduino/E1.31 process.
I was hoping to maybe build a little "pixel tester" for the WS2811 and the GECE's (thats really all I have).
Any thoughts?
Again... THANK YOU very much for the work you put into this project and for sharing with us. I know having the working code makes understanding what all its doing a lot simpler for me.
Bob
 

bob_moody

Apprentice elf
Joined
Oct 20, 2012
Messages
86
Location
Huntsville, TX
Well it took a little while (most of it my own fault... running LOR CP and Xlights at the same time and not realizing it) but I did manage to modify Elnino's script to support GECE pixels.
Other than converting library calls, my biggest obstacle was the bit color. The GECE's are 12 bit (4-bits each for R,G, and B). So everything worked fine when I slid the slider on Xlights from 0 to 15, After that it was generating all sorts of interesting colors.
I found that I could bit switch by using bit swapping (x >> y ). i just rotated to the right 4 bits each of the colors coming in from the sACN packet and that appears to have resolved the issue. Now 0 to 255 on xLights results in only the single color being presented.
I havent looked in to it yet, but I'm hoping this sketch will fit on an Arduino mini allowing me to create a couple of those $10 utility controllers.
If any one is interested I will post the script. Thanks again to Elnino for the basis to start this project.
Bob

Quick Update... It works in LOR .. sorta but not really... In Xlights, its rock solid. In LOR, the lights seem to be pulsing at the packet rate. That is to say the pin 13 LED on the Arduino is blinking and its in sync with the blicking of the lights. Although the color changes are working. Its in a permanent blink .. ARGGHHHH..
 

damo1271

Full time elf
Joined
Oct 12, 2011
Messages
202
Location
Adelaide
I have to add my thanks as well. I used the arduino with 200 pixels controlled from Vixen 3 / FPP with no significant issues. I am also planning to modify the code to use the arduino as a standalone pixel string tester and will post the code here once i get around to modifying it.
 

ynotmoody

New elf
Joined
Aug 10, 2015
Messages
1
Please can you Help me.

I have set up Wiznet/Arduino Ethernet version works lovely thank you

problem I'm having is with ENC28J60 Version can not upload getting error:

Arduino: 1.6.5 (Windows 8.1), Board: "Arduino Uno"

sketch_aug09c.ino: In function 'void setup()':
sketch_aug09c:114: error: invalid conversion from 'void (*)(word, byte*, const char*, word) {aka void (*)(unsigned int, unsigned char*, const char*, unsigned int)}' to 'UdpServerCallback {aka void (*)(unsigned int, unsigned char*, unsigned int, const char*, unsigned int)}' [-fpermissive]
In file included from sketch_aug09c.ino:41:0:
C:\Users\Tony\Documents\Arduino\libraries\ethercard-master/EtherCard.h:489:17: error: initializing argument 1 of 'static void EtherCard::udpServerListenOnPort(UdpServerCallback, uint16_t)' [-fpermissive]
static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port);
^
invalid conversion from 'void (*)(word, byte*, const char*, word) {aka void (*)(unsigned int, unsigned char*, const char*, unsigned int)}' to 'UdpServerCallback {aka void (*)(unsigned int, unsigned char*, unsigned int, const char*, unsigned int)}' [-fpermissive]
 

elnino

Full time elf
Joined
Aug 1, 2014
Messages
142
There was a change to the ethercard library which broke it. Go back to an earlier version of Ethercard library and it will compile.
 
Top