Code:
//Running e131 through OctoWS2811 powered by fastled on Teensy 4.1
//Brian Amos <brian@amoscomputing.com>
#include <OctoWS2811.h>
#include <FastLED.h>
#include <NativeEthernet.h>
#include <Arduino.h>
//debug mode adds some serial feedback during testing. Remove this line for production code
#define DEBUG_MODE 1
//DEFINES for at Compile time.
//Leave this alone. At current a full e1.31 frame is 636 bytes..
#define ETHERNET_BUFFER 636 //540 is artnet leave at 636 for e1.31
#define NUM_LEDS_PER_STRIP 340 //170 //680 default
#define NUM_STRIPS 2 //make sure pin list is accurate
#define DMX_SUBNET 0
int unsigned DMX_UNIVERSE = 1; //**Start** universe 1, 9, 17, 25, 33, 41
int unsigned UNIVERSE_COUNT = 4; //How Many Universes 8, 8, 8, 4, 8, 8
int unsigned UNIVERSE_LAST = 4; // List the last universe typically its sequencially from start but does not have to. 8, 16, 24, 28, 32, 40
int unsigned CHANNEL_COUNT = 510; //max channels per dmx packet
byte unsigned LEDS_PER_UNIVERSE = 170; // Max RGB pixels //170 default
int unsigned NUM_LEDS = UNIVERSE_COUNT * LEDS_PER_UNIVERSE; // with current fastLED and OctoWs2811 libraries buffers... do not go higher than this - Runs out of SRAM
//ethernet setup
unsigned char packetBuffer[ETHERNET_BUFFER];
int c = 0;
float fps = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
EthernetUDP Udp;
uint8_t mac[6]; //this is determined from the Teensy 4.1 board.
IPAddress ip(192,168,1,10); //static ip address of board
#define UDP_PORT 5568 //E1.31 UDP Port Number
//set up OctoWS2811 and FastLED for Teensy 4.1
const int numPins = NUM_STRIPS;
byte pinList[numPins] = {19, 18};
const int ledsPerStrip = NUM_LEDS_PER_STRIP; //this should be NUM_LEDS / numPins => VERIFY THIS MATCHES THE HARDWARE!! //should also be multiples of a single universe of leds
CRGB rgbarray[numPins * ledsPerStrip];
// These buffers need to be large enough for all the pixels.
// The total number of pixels is "ledsPerStrip * numPins".
// Each pixel needs 3 bytes, so multiply by 3. An "int" is
// 4 bytes, so divide by 4. The array is created using "int"
// so the compiler will align it to 32 bit memory.
DMAMEM int displayMemory[ledsPerStrip * numPins * 3 / 4];
int drawingMemory[ledsPerStrip * numPins * 3 / 4];
OctoWS2811 octo(ledsPerStrip, displayMemory, drawingMemory, WS2811_GRB | WS2811_800kHz, numPins, pinList);
template <EOrder RGB_ORDER = RGB,
uint8_t CHIP = WS2811_800kHz>
class CTeensy4Controller : public CPixelLEDController<RGB_ORDER, 8, 0xFF>
{
OctoWS2811 *pocto;
public:
CTeensy4Controller(OctoWS2811 *_pocto)
: pocto(_pocto){};
virtual void init() {}
virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> &pixels)
{
uint32_t i = 0;
while (pixels.has(1))
{
uint8_t r = pixels.loadAndScale0();
uint8_t g = pixels.loadAndScale1();
uint8_t b = pixels.loadAndScale2();
pocto->setPixel(i++, r, g, b);
pixels.stepDithering();
pixels.advanceData();
}
pocto->show();
}
};
CTeensy4Controller<RGB, WS2811_800kHz> *pcontroller;
void setup() {
Serial.begin(115200);
delay(10);
teensyMAC(mac);
static char teensyMac[23];
sprintf(teensyMac, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.print("MAC: ");
Serial.println(teensyMac);
Ethernet.begin(mac, ip);
Serial.print("IP Address: ");
Serial.println(Ethernet.localIP());
Serial.print("MAC: ");
Serial.println(teensyMac);
Udp.begin(UDP_PORT);
octo.begin();
pcontroller = new CTeensy4Controller<RGB, WS2811_800kHz>(&octo);
FastLED.addLeds(pcontroller, rgbarray, numPins * ledsPerStrip);
FastLED.setBrightness(50);
initTest();
Serial.println("Test Sequence Complete");
} //end setup
static inline void fps2(const int seconds){
// Create static variables so that the code and variables can
// all be declared inside a function
static unsigned long lastMillis;
static unsigned long frameCount;
static unsigned int framesPerSecond;
// It is best if we declare millis() only once
unsigned long now = millis();
frameCount ++;
if (now - lastMillis >= seconds * 1000) {
framesPerSecond = frameCount / seconds;
Serial.print("FPS @ ");
Serial.println(framesPerSecond);
frameCount = 0;
lastMillis = now;
}
}
static inline void pixelrefresh(const int syncrefresh){
// Create static variables so that the code and variables can
// all be declared inside a function
static unsigned long frametimestart;
static unsigned long frametimeend;
static unsigned long frametimechk;
static unsigned long frameonce;
unsigned long now = micros();
//start frame time
frametimestart = now;
//Serial.println(frametimechk);
//If we have framed no need to frame again update time to most recent
if (syncrefresh == 1){
frametimeend = frametimestart;
frameonce = 1;
}
//If we havent framed this will increment via time and at some point will be true,
//if so we need to frame to clear out any buffer and the hold off untill
//we receive our next valid dmx packet. We use the pixel protocol to get a general rule of timing to compare to.
frametimechk = frametimestart - frametimeend;
// num leds time 30us + 300us reset to simulate the time it would take to write out pixels.
//this should help us not loop to fast and risk premature framing and jeopordize ethernet buffer
if (frametimechk >= (NUM_LEDS * 30) + 300){
frametimeend = frametimestart;
if (frameonce == 1){
octo.show();
Serial.println ("Partial framing detected");
frameonce = 0;
}
}
}
void sacnDMXReceived(unsigned char* pbuff, int count) {
Serial.print("sacn DMX Received: Subnet=");
Serial.print(pbuff[113]);
Serial.print(", Universe=");
Serial.print(pbuff[114]);
Serial.print(", Sequence=");
Serial.println(pbuff[111]);
static unsigned long uniloopcount;
if (count > CHANNEL_COUNT) count = CHANNEL_COUNT;
byte b = pbuff[113]; //DMX Subnet
if ( b == DMX_SUBNET) {
b = pbuff[114]; //DMX Universe
byte s = pbuff[111]; //sequence
static unsigned long ls; // Last Sequence
if (s > ls){
uniloopcount = 0;
ls = s;
}
//turn framing LED OFF
// digitalWrite(4, HIGH);
// Serial.print("UNI ");
// Serial.println(count );
// Serial.println(s);
if ( b >= DMX_UNIVERSE && b <= UNIVERSE_LAST) {
//Serial.println(b );
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];
octo.setPixel(ledNumber, charValueR,charValueG,charValueB); //RBG GRB
//Serial.println(ledNumber);
ledNumber++;
}
}
}
}
uniloopcount ++;
Serial.print("UNILOOP");
Serial.println(uniloopcount);
//if (b == UNIVERSE_LAST){
if (uniloopcount >= UNIVERSE_COUNT){
//Turn Framing LED ON
// digitalWrite(4, LOW);
octo.show();
pixelrefresh(1);
uniloopcount = 0;
//Frames Per Second Function fps(every_seconds)
fps2(5);
}
}
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.clear(); //clear led assignments
// LEDS.showColor(CRGB(0,0,0)); //turn off all pixels to start
// delay(500);
LEDS.showColor(CRGB(255, 0, 0)); //turn all pixels on red
delay(3000);
LEDS.showColor(CRGB(0, 255, 0)); //turn all pixels on green
delay(3000);
LEDS.showColor(CRGB(0, 0, 255)); //turn all pixels on blue
delay(3000);
LEDS.showColor(CRGB(0,0,0)); //turn off all pixels to start
// LEDS.showColor(CRGB(50, 50, 50)); //turn all pixels off
/*
//process dot trace
for (int n=0; n < NUM_LEDS_PER_STRIP*NUM_STRIPS; n++){
octo.setPixel(n,100,0,0);
octo.setPixel(n-2,0,0,0);
octo.show();
delay(10);
}
for (int n=0; n < NUM_LEDS_PER_STRIP*NUM_STRIPS; n++){
octo.setPixel(n,0,100,0);
octo.setPixel(n-2,0,0,0);
octo.show();
delay(10);
}
for (int n=0; n < NUM_LEDS_PER_STRIP*NUM_STRIPS; n++){
octo.setPixel(n,0,0,100);
octo.setPixel(n-2,0,0,0);
octo.show();
delay(10);
}
*/
}
//define mac address of board
void teensyMAC(uint8_t *mac) {
static char teensyMac[23];
#if defined(HW_OCOTP_MAC1) && defined(HW_OCOTP_MAC0)
Serial.println("using HW_OCOTP_MAC* - see https://forum.pjrc.com/threads/57595-Serial-amp-MAC-Address-Teensy-4-0");
for(uint8_t by=0; by<2; by++) mac[by]=(HW_OCOTP_MAC1 >> ((1-by)*8)) & 0xFF;
for(uint8_t by=0; by<4; by++) mac[by+2]=(HW_OCOTP_MAC0 >> ((3-by)*8)) & 0xFF;
#define MAC_OK
#else
mac[0] = 0x04;
mac[1] = 0xE9;
mac[2] = 0xE5;
uint32_t SN=0;
__disable_irq();
#if defined(HAS_KINETIS_FLASH_FTFA) || defined(HAS_KINETIS_FLASH_FTFL)
Serial.println("using FTFL_FSTAT_FTFA - vis teensyID.h - see https://github.com/sstaub/TeensyID/blob/master/TeensyID.h");
FTFL_FSTAT = FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL;
FTFL_FCCOB0 = 0x41;
FTFL_FCCOB1 = 15;
FTFL_FSTAT = FTFL_FSTAT_CCIF;
while (!(FTFL_FSTAT & FTFL_FSTAT_CCIF)) ; // wait
SN = *(uint32_t *)&FTFL_FCCOB7;
#define MAC_OK
#elif defined(HAS_KINETIS_FLASH_FTFE)
Serial.println("using FTFL_FSTAT_FTFE - vis teensyID.h - see https://github.com/sstaub/TeensyID/blob/master/TeensyID.h");
kinetis_hsrun_disable();
FTFL_FSTAT = FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL;
*(uint32_t *)&FTFL_FCCOB3 = 0x41070000;
FTFL_FSTAT = FTFL_FSTAT_CCIF;
while (!(FTFL_FSTAT & FTFL_FSTAT_CCIF)) ; // wait
SN = *(uint32_t *)&FTFL_FCCOBB;
kinetis_hsrun_enable();
#define MAC_OK
#endif
__enable_irq();
for(uint8_t by=0; by<3; by++) mac[by+3]=(SN >> ((2-by)*8)) & 0xFF;
#endif
#ifdef MAC_OK
sprintf(teensyMac, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.println(teensyMac);
#else
Serial.println("ERROR: could not get MAC");
#endif
}
void loop() {
//Process packets
int packetSize = Udp.parsePacket(); //Read UDP packet count
if(packetSize){
Serial.println(packetSize);
Udp.read(packetBuffer,ETHERNET_BUFFER); //read UDP packet
int count = checkACNHeaders(packetBuffer, packetSize);
if (count) {
// Serial.print("packet size first ");
// Serial.println(packetSize);
sacnDMXReceived(packetBuffer, count); //process data function
}
}
pixelrefresh(0);
}
Bookmarks