Skip to content

Instantly share code, notes, and snippets.

@hsiboy
Last active April 11, 2023 22:13
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hsiboy/db54c9bf478c3d667170 to your computer and use it in GitHub Desktop.
Save hsiboy/db54c9bf478c3d667170 to your computer and use it in GitHub Desktop.
/*
d8888 .d8888b. 888b 888
d88888 d88P Y88b 8888b 888
d88P888 888 888 88888b 888
.d8888b d88P 888 888 888Y88b 888
88K d88P 888 888 888 Y88b888
"Y8888b. d88P 888 888 888 888 Y88888
X88 d8888888888 Y88b d88P 888 Y8888
88888P' d88P 888 "Y8888P" 888 Y888
.d8888b.
d88P Y88b
888
.d88P
.od888P"
d88P"
888"
888888888
888 888 .d8888b. .d8888b. .d8888b. d888 d888
888 o 888 d88P Y88b d88P Y88b d88P Y88b d8888 d8888
888 d8b 888 Y88b. 888 Y88b. d88P 888 888
888 d888b 888 "Y888b. .d88P "Y88888" 888 888
888d88888b888 "Y88b. .od888P" .d8P""Y8b. 888 888
88888P Y88888 "888 d88P" 888 888 888 888
8888P Y8888 Y88b d88P 888" Y88b d88P 888 888
888P Y888 "Y8888P" 888888888 "Y8888P" 8888888 8888888
created January 4th, 2014 by Claude Heintz
modified by: Jared Alexander
modified by: Stuart Taylor
This code is in the public domain.
sACN E 1.31 is a public standard published by the PLASA technical standards program
http://tsp.plasa.org/tsp/documents/published_docs.php
Streaming ACN (sACN) is a standard protocol developed by ESTA to efficiently transport DMX universes over the network.
It is comparable to ArtNET in many aspects. One nice thing is the multicast option allowing very easy configuration.
sACN is a popular protocol to control large number of RGB LEDs.
Requires Arduino Ethernet Shield (Wiznet W5100 W5200 ENC28J60)
Modifed to use the lastest version of EthernetUdp library included in Arduino 1.0.5-r2. No need to install other libraries.
Receives E1.31 date (sACN) and outputs to WS2811 pixels using FastLED library.
Includes timer to run standby program when not receiving data and init program to test pixels each time board boots.
*/
// #define DEBUG
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.println (x)
#else
#define DEBUG_PRINT(x)
#endif
#include <SPI.h> // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <FastLED.h>
#define LED_PIN 6 // physical pin 12 on the chip - PD6
#define LED_TYPE WS2811
#define COLOR_ORDER RGB
#define MAX_BRIGHTNESS 128 //Default LED Brightness
#define NUM_LEDS 50 //number of pixels
CRGB leds[NUM_LEDS]; //pixel array
byte current_hue; //hue value for rainbow effect
byte ledNumber; //current led Pixel being used for rainbow effect
// enter desired universe and subnet (sACN first universe is 1)
#define DMX_SUBNET 0
#define DMX_UNIVERSE 1
#define SACN_BUFFER_MAX 640
#define SACN_PORT 5568
const byte channelwidth = 3; //3 channels per pixel
byte channel; //channel number for each pixel RGB
volatile byte currentcounter = 0; //counter for data reception
byte previouscounter = 0; //counter for data reception
unsigned long now = 0; //current time value
//the initial ip and MAC address will get changed by beginMulti
//the multicast ip address should correspond to the desired universe/subnet
byte mac[] = {0x90, 0xA2, 0xDA, 0x0F, 0x2A, 0xBC}; //MAC address of ethernet shield
IPAddress ip(192, 168, 1, 120); //IP address of ethernet shield
IPAddress mip(239, 255, 0, 1); //default multicast IP for E1.31
// buffers for receiving and sending data
unsigned char packetBuffer[SACN_BUFFER_MAX]; //buffer to hold incoming packet,
// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
//setup initializes Ethernet, opens the UDP port and initializes pixels
void setup()
{
delay(3000); // lets us re-program, by allowing arduino software time to interrupt.
FastLED.addLeds<LED_TYPE,LED_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(MAX_BRIGHTNESS);
Ethernet.begin(mac,ip);
Udp.begin(SACN_PORT);
#ifdef DEBUG
Serial.begin(9600);
#endif
lampTest(); //test to make sure pixels are working
}
void sacnDMXReceived(unsigned char* pbuff, int count)
{
DEBUG_PRINT ("DMX Packet Received");
if ( pbuff[113] == DMX_SUBNET )
{
if ( pbuff[114] == DMX_UNIVERSE )
{
int addressOffset = 125;
if ( pbuff[addressOffset] == 0 )
{ //start code must be 0
channel = 1; //reset DMX channel assignment to 1 each time through loop.
for (int i = 0; i < NUM_LEDS; i++) //loop to assign 3 channels to each pixel
{
leds[i] = CRGB(pbuff[addressOffset + channel], pbuff[addressOffset + (channel +1)], pbuff[addressOffset + (channel +2)]);
channel +=channelwidth; //increase last channel number by channel width
}
}
}
}
FastLED.show(); //send data to pixels
}
//checks to see if packet is E1.31 data
/*
int checkACNHeaders(unsigned char* messagein, int messagelength)
{
if ( messagein[1] == 0x10 )
{ //header
String test = String((char*)&messagein[4]);
if ( test.equals("ASC-E1.17") )
{
int rootsizeflags = messagein[16] * 256 + messagein[17];
if ( (0x7000 + messagelength-16) == rootsizeflags )
{
int framingsizeflags = messagein[38] * 256 + messagein[39];
if ( (0x7000 + messagelength-38) == framingsizeflags )
{
int dmpsizeflags = messagein[115] * 256 + messagein[116];
if ( (0x7000 + messagelength-115) == dmpsizeflags )
{
int addresscount = messagein[123] * 256 + messagein[124]; // number of addresses plus start code
if ( (messagelength-125) == addresscount )
{
return addresscount;
}
}
}
}
}
}
return 0;
}
*/
int checkACNHeaders(const char* messagein, int messagelength) {
if ( messagein[1] == 0x10 && messagein[4] == 0x41 && messagein[12] == 0x37) {
// 0x41 = 'A'
// 0x37 = '7'
int addresscount = messagein[123] * 256 + messagein[124]; // number of values plus start code
return addresscount -1; //Return how many values are in the packet.
}
return 0;
}
void loop()
{
int packetSize = Udp.parsePacket(); //store UPD packet
//timer to check if receiving data
if(currentcounter != previouscounter) //has the counter changed?
{
now = millis(); //store the time since the counter has increased
previouscounter = currentcounter; //set the previous value equal to the current value
}
if((millis() - now) > 5000) //is the time since the counter changed greater than 5 seconds?
{
noReceive(); //not receiving data. Run no receive function
}
if(packetSize)
{
Udp.read(packetBuffer,SACN_BUFFER_MAX); //read UDP packet
int count = checkACNHeaders(packetBuffer, packetSize);
if (count)
{
sacnDMXReceived(packetBuffer, count); //process data function
currentcounter++; //increase counter by 1 each time through
}
}
}
void initTest() //runs at board boot to make sure pixels are working
{
FastLED.setBrightness(255); // set to full power
FastLED.clear(); // on power up, one or more leds can be in a funky state.
DEBUG_PRINT ("Lamp Test:");
ledsSolid(CRGB::Red);
FastLED.show();
FastLED.delay(2000);
ledsSolid(CRGB::Green);
DEBUG_PRINT ("Green");
FastLED.show();
FastLED.delay(2000);
ledsSolid(CRGB::Blue);
DEBUG_PRINT ("Blue");
FastLED.show();
FastLED.delay(2000);
ledsSolid(CRGB::Yellow);
DEBUG_PRINT ("Yellow");
FastLED.show();
FastLED.delay(2000);
ledsSolid(CRGB::Violet);
DEBUG_PRINT ("Violet");
FastLED.show();
FastLED.delay(2000);
ledsSolid(CRGB::White);
DEBUG_PRINT ("White - Check Power!");
FastLED.show();
FastLED.delay(2000);
FastLED.clear();
DEBUG_PRINT ("Rainbows!");
fill_rainbow(leds, NUM_LEDS, CRGB::White);
FastLED.show();
FastLED.delay(2000);
Serial.println("Show Time...");
FastLED.clear();
FastLED.setBrightness(MAX_BRIGHTNESS);
}
void noReceive()
{
//simple rainbow fill
fill_rainbow(leds, NUM_LEDS, CRGB::White);
FastLED.show();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment