Another Arduino pixel controller

Discussion in 'DMX, E1.31 & Networking' started by elnino, Nov 4, 2014.

  1. elnino

    elnino Full Time Elf

    Joined:
    Aug 1, 2014
    Messages:
    137
    Likes Received:
    0
    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 by Andrew Huxtable (andrew@hux.net.au)
    // 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) {
      //Do some VERY basic checks to see if it's an E1.31 packet.
      //Bytes 4 to 12 of an E1.31 Packet contain "ACN-E1.17"
      //Only checking for the A and the 7 in the right places as well as 0x10 as the header.
      //Technically this is outside of spec and could cause problems but its enough checks for us
      //to determine if the packet should be tossed or used.
      //This improves the speed of packet processing as well as reducing the memory overhead.
      //On an Isolated network this should never be a problem....
      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 by Andrew Huxtable (andrew@hux.net.au)
    // 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]
    //
    // 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 <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) {
      //Do some VERY basic checks to see if it's an E1.31 packet.
      //Bytes 4 to 12 of an E1.31 Packet contain "ACN-E1.17"
      //Only checking for the A and the 7 in the right places as well as 0x10 as the header.
      //Technically this is outside of spec and could cause problems but its enough checks for us
      //to determine if the packet should be tossed or used.
      //This improves the speed of packet processing as well as reducing the memory overhead.
      //On an Isolated network this should never be a problem....
      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
    }
    

    This is my Pixel tree running off of 6 X Adruino Uno clones with Wiznet Ethernet shield.
    http://youtu.be/h0Cqzan1uDU


    My 80 pixel Starburst:
    http://youtu.be/ekL2u8TyuLQ


    Enjoy!
     
  2. damo1271

    damo1271 Full Time Elf Generous Elf

    Joined:
    Oct 12, 2011
    Messages:
    193
    Likes Received:
    3
    Location:
    Adelaide
    Find Me On:
    Excellent, I can't wait to try this. But China post is so slow with my arduinos :mad:
     
  3. JPB

    JPB Full Time Elf

    Joined:
    May 13, 2010
    Messages:
    304
    Likes Received:
    0
    Location:
    Glenwood
    I loaded this into my Uno on the weekend with the ENC28J60 ethernet module and it is brilliant. Great work Elnino

    Jon
     
  4. damo1271

    damo1271 Full Time Elf Generous Elf

    Joined:
    Oct 12, 2011
    Messages:
    193
    Likes Received:
    3
    Location:
    Adelaide
    Find Me On:
    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 :(
     
  5. JPB

    JPB Full Time Elf

    Joined:
    May 13, 2010
    Messages:
    304
    Likes Received:
    0
    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?
     
  6. damo1271

    damo1271 Full Time Elf Generous Elf

    Joined:
    Oct 12, 2011
    Messages:
    193
    Likes Received:
    3
    Location:
    Adelaide
    Find Me On:
    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
     
  7. damo1271

    damo1271 Full Time Elf Generous Elf

    Joined:
    Oct 12, 2011
    Messages:
    193
    Likes Received:
    3
    Location:
    Adelaide
    Find Me On:
    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.
     
  8. JPB

    JPB Full Time Elf

    Joined:
    May 13, 2010
    Messages:
    304
    Likes Received:
    0
    Location:
    Glenwood
    It is a great feeling when it works isn't it.
     
  9. damo1271

    damo1271 Full Time Elf Generous Elf

    Joined:
    Oct 12, 2011
    Messages:
    193
    Likes Received:
    3
    Location:
    Adelaide
    Find Me On:
    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.
     
  10. OP
    OP
    elnino

    elnino Full Time Elf

    Joined:
    Aug 1, 2014
    Messages:
    137
    Likes Received:
    0

    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.
     
  11. bob_moody

    bob_moody Apprentice Elf

    Joined:
    Oct 20, 2012
    Messages:
    86
    Likes Received:
    0
    Location:
    Huntsville, TX
    Find Me On:
    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
     
  12. bob_moody

    bob_moody Apprentice Elf

    Joined:
    Oct 20, 2012
    Messages:
    86
    Likes Received:
    0
    Location:
    Huntsville, TX
    Find Me On:
    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..
     
  13. damo1271

    damo1271 Full Time Elf Generous Elf

    Joined:
    Oct 12, 2011
    Messages:
    193
    Likes Received:
    3
    Location:
    Adelaide
    Find Me On:
    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.
     
  14. ynotmoody

    ynotmoody New Elf

    Joined:
    Aug 10, 2015
    Messages:
    1
    Likes Received:
    0
    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]
     
  15. OP
    OP
    elnino

    elnino Full Time Elf

    Joined:
    Aug 1, 2014
    Messages:
    137
    Likes Received:
    0
    There was a change to the ethercard library which broke it. Go back to an earlier version of Ethercard library and it will compile.
     

Share This Page