├── MegaPixel_E1.31.ino └── README.md /MegaPixel_E1.31.ino: -------------------------------------------------------------------------------- 1 | 2 | // E1.31 Receiver and pixel controller by Chris Rees (crees@bearrivernet.net) for the Teensy 3.2 This code was modified from 3 | // Andrew Huxtable base code. (andrew@hux.net.au) 4 | // This code may be freely distributed and used as you see fit for non-profit 5 | // purposes and as long as the original author is credited and it remains open 6 | // source 7 | // 8 | // Please configure your Lighting product to use Unicast to the IP the device is given from your DHCP server 9 | // Multicast is not currently supported at the moment 10 | 11 | 12 | // You will need the, Teensy utility installed with the added Ethernet One Socket and FastLed Libraries from: 13 | // [url]https://www.pjrc.com/teensy/teensyduino.html[/url] 14 | // [url]https://github.com/mrrees/MegaPixel-One-Socket-Ethernet[/url] 15 | // [url]https://github.com/FastLED/FastLED/releases[/url] 16 | // 17 | // Please note the one socket library may throw errors in compiling. The error was in realation to the chip speed detection and using the 18 | // correct SPI speed. 19 | // 20 | // The Teensy with the Octows2811 and FastLED will allow up to 5440 Pixels (32 Universes) to run. One thing to note is if you desire 21 | // high frame rates your pixel count must not exceed over 680 Pixels per Octo Pin. The reason why is the ammount of time to write out to 22 | // these LED's takes time to shift from one LED to the next. The more LED's per SPI or Octo Pin the more time it takes and the less frame 23 | // rate you will acheive. In the Pixel Controller Wolrd 680 per SPI port is the desired balance. For the Teensy this is a perfect balance 24 | // any more pixels and memory starts to become an issue. Those who whish to push more pixels per port can do so but must sacrifice the 25 | // fastLED and or Octows2811 libraries to free up buffer space.. but your on your own and we welcome you to share your improved methods 26 | // with the community! 27 | 28 | 29 | // in the code structure there is some serial feedback. Please note enableing serial feedback will interrupt with the pixel performance 30 | // and will slow it down. use only for debugging and omit during production run. 31 | 32 | #include 33 | #include 34 | #include 35 | #define USE_OCTOWS2811 36 | #include 37 | #include 38 | #include 39 | 40 | 41 | //********************************************************************************* 42 | 43 | 44 | // enter desired universe and subnet (sACN first universe is 1) 45 | #define DMX_SUBNET 0 46 | //#define DMX_UNIVERSE 1 //**Start** universe 47 | 48 | 49 | // Set a different MAC address for each controller IMPORTANT!!!! you can change the last value but make sure its HEX!... 50 | byte mac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x21 }; 51 | 52 | //DEFINES for at Compile time. 53 | //Leave this alone. At current a full e1.31 frame is 636 bytes.. 54 | #define ETHERNET_BUFFER 636 //540 is artnet leave at 636 for e1.31 55 | #define NUM_LEDS_PER_STRIP 680 //170 56 | #define NUM_STRIPS 8 57 | 58 | 59 | ///GLOBALS 60 | int unsigned DMX_UNIVERSE = 1; //**Start** universe 1, 9, 17, 25, 33, 41 61 | int unsigned UNIVERSE_COUNT = 32; //How Many Universes 8, 8, 8, 4, 8, 8 62 | int unsigned UNIVERSE_LAST = 32; // List the last universe typically its sequencially from start but does not have to. 8, 16, 24, 28, 32, 40 63 | int unsigned CHANNEL_COUNT = 510; //max channels per dmx packet 64 | byte unsigned LEDS_PER_UNIVERSE = 170; // Max RGB pixels 65 | 66 | // 67 | 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 68 | 69 | /// 70 | 71 | 72 | 73 | //*********************************************************** 74 | // BEGIN Dont Modify unless you know what your doing below 75 | //*********************************************************** 76 | 77 | // Define the array of leds 78 | 79 | CRGB leds[NUM_STRIPS * NUM_LEDS_PER_STRIP]; 80 | 81 | // Pin layouts on the teensy 3: 82 | // OctoWS2811: 2,14,7,8,6,20,21,5 83 | 84 | 85 | unsigned char packetBuffer[ETHERNET_BUFFER]; 86 | int c = 0; 87 | float fps = 0; 88 | unsigned long currentMillis = 0; 89 | unsigned long previousMillis = 0; 90 | 91 | EthernetUDP Udp; 92 | 93 | void setup() { 94 | 95 | //byte ipadd[4] ; //Variable to store data read from EEPROM. 96 | //unsigned int eeAddress = 39; //EEPROM address to start reading from 97 | //EEPROM.get( eeAddress, ipadd ); 98 | 99 | //******************************************************* 100 | // ethernet interface ip address 101 | //IPAddress ip(ipadd[0], ipadd[1], ipadd[2], ipadd[3]); //IP address of ethernet shield 102 | IPAddress ip(192, 168, 2, 21); //IP address of ethernet shield 103 | 104 | //IPAddress mip(239,255,0,1); //multicast IP 105 | //******************************************************* 106 | 107 | // E1.31 is UDP. One socket library will only allow one protocol to be defined. 108 | 109 | 110 | 111 | 112 | //WIZNET RESET AND INITIALIZE 113 | pinMode(9, OUTPUT); 114 | digitalWrite(9, LOW); // reset the WIZ820io 115 | delay(10); 116 | digitalWrite(9, HIGH); // reset the WIZ820io 117 | 118 | //SD CARD INITIALIZE 119 | //pinMode(10, OUTPUT); // For SD Card Stuff 120 | //digitalWrite(10, HIGH); // de-select WIZ820io 121 | //pinMode(4, OUTPUT); //SD Card Stuff 122 | //digitalWrite(4, HIGH); // de-select the SD Card 123 | 124 | //Serial Port Stuff 125 | Serial.begin(115200); 126 | delay(10); 127 | 128 | 129 | // Using different LEDs or colour order? Change here... 130 | // ******************************************************** 131 | LEDS.addLeds(leds, NUM_LEDS_PER_STRIP); 132 | LEDS.setBrightness(50); //value should be 0-255 Very Bright after 100 default is 50 to save on current and eyes! 133 | // ******************************************************** 134 | 135 | //pins 3,4,22 are to the RGB Status LED 136 | 137 | // ******************************************************** 138 | //Ethernet.init(1); //-> 1 Socket with 16k RX/TX buffer for ethernet3 139 | 140 | Ethernet.begin(mac,ip); 141 | 142 | //int success = Udp.beginMulticast(mip, 5568); 143 | //Serial.print( "begin, success: " ); 144 | //Serial.println( success ); 145 | 146 | Udp.begin(5568); 147 | 148 | //DEFINE AND Turn Framing LED OFF 149 | pinMode(4, OUTPUT); 150 | digitalWrite(4, HIGH); 151 | //DEFINE AND TURN STATUS LED ON 152 | pinMode(3, OUTPUT); 153 | 154 | digitalWrite(3, LOW); 155 | delay(9000); 156 | //Turn Status LED OFF 157 | digitalWrite(3, HIGH); 158 | // ******************************************************** 159 | Serial.print("server is at "); 160 | Serial.println(Ethernet.localIP()); 161 | //Serial.println(F_BUS); 162 | 163 | //Once the Ethernet is initialised, run a test on the LEDs 164 | //initTest(); 165 | } 166 | 167 | 168 | 169 | static inline void fps2(const int seconds){ 170 | // Create static variables so that the code and variables can 171 | // all be declared inside a function 172 | static unsigned long lastMillis; 173 | static unsigned long frameCount; 174 | static unsigned int framesPerSecond; 175 | 176 | // It is best if we declare millis() only once 177 | unsigned long now = millis(); 178 | frameCount ++; 179 | if (now - lastMillis >= seconds * 1000) { 180 | framesPerSecond = frameCount / seconds; 181 | 182 | Serial.print("FPS @ "); 183 | Serial.println(framesPerSecond); 184 | frameCount = 0; 185 | lastMillis = now; 186 | } 187 | 188 | } 189 | 190 | static inline void pixelrefresh(const int syncrefresh){ 191 | // Create static variables so that the code and variables can 192 | // all be declared inside a function 193 | static unsigned long frametimestart; 194 | static unsigned long frametimeend; 195 | static unsigned long frametimechk; 196 | static unsigned long frameonce; 197 | unsigned long now = micros(); 198 | 199 | 200 | //start frame time 201 | frametimestart = now; 202 | 203 | //Serial.println(frametimechk) 204 | //If we have framed no need to frame again update time to most recent 205 | if (syncrefresh == 1){ 206 | frametimeend = frametimestart; 207 | frameonce = 1; 208 | } 209 | 210 | //If we havent framed this will increment via time and at some point will be true, 211 | //if so we need to frame to clear out any buffer and the hold off untill 212 | //we receive our next valid dmx packet. We use the pixel protocol to get a general rule of timing to compare to. 213 | 214 | frametimechk = frametimestart - frametimeend; 215 | // num leds time 30us + 300us reset to simulate the time it would take to write out pixels. 216 | //this should help us not loop to fast and risk premature framing and jeopordize ethernet buffer 217 | if (frametimechk >= (NUM_LEDS * 30) + 300){ 218 | frametimeend = frametimestart; 219 | 220 | 221 | if (frameonce == 1){ 222 | LEDS.show(); 223 | Serial.println ("Partial framing detected"); 224 | frameonce = 0; 225 | } 226 | 227 | } 228 | 229 | } 230 | 231 | 232 | 233 | void sacnDMXReceived(unsigned char* pbuff, int count) { 234 | static unsigned long uniloopcount; 235 | if (count > CHANNEL_COUNT) count = CHANNEL_COUNT; 236 | byte b = pbuff[113]; //DMX Subnet 237 | if ( b == DMX_SUBNET) { 238 | b = pbuff[114]; //DMX Universe 239 | byte s = pbuff[111]; //sequence 240 | static unsigned long ls; // Last Sequence 241 | if (s > ls){ 242 | uniloopcount = 0; 243 | ls = s; 244 | } 245 | //turn framing LED OFF 246 | digitalWrite(4, HIGH); 247 | //Serial.print("UNI "); 248 | //Serial.println(count ); 249 | //Serial.println(s); 250 | if ( b >= DMX_UNIVERSE && b <= UNIVERSE_LAST) { 251 | //Serial.println(b ); 252 | if ( pbuff[125] == 0 ) { //start code must be 0 253 | int ledNumber = (b - DMX_UNIVERSE) * LEDS_PER_UNIVERSE; 254 | // sACN packets come in seperate RGB but we have to set each led's RGB value together 255 | // this 'reads ahead' for all 3 colours before moving to the next led. 256 | //Serial.println("*"); 257 | for (int i = 126;i < 126+count;i = i + 3){ 258 | byte charValueR = pbuff[i]; 259 | byte charValueG = pbuff[i+1]; 260 | byte charValueB = pbuff[i+2]; 261 | leds[ledNumber] = CRGB(charValueR,charValueG,charValueB); //RBG GRB 262 | //Serial.println(ledNumber); 263 | ledNumber++; 264 | } 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | } 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | } 282 | 283 | 284 | 285 | } 286 | 287 | uniloopcount ++; 288 | //Serial.print("UNILOOP"); 289 | //Serial.println(uniloopcount); 290 | 291 | //if (b == UNIVERSE_LAST){ 292 | if (uniloopcount >= UNIVERSE_COUNT){ 293 | //Turn Framing LED ON 294 | digitalWrite(4, LOW); 295 | LEDS.show(); 296 | pixelrefresh(1); 297 | uniloopcount = 0; 298 | //Frames Per Second Function fps(every_seconds) 299 | fps2(5); 300 | } 301 | 302 | } 303 | 304 | 305 | int checkACNHeaders(unsigned char* messagein, int messagelength) { 306 | //Do some VERY basic checks to see if it's an E1.31 packet. 307 | //Bytes 4 to 12 of an E1.31 Packet contain "ACN-E1.17" 308 | //Only checking for the A and the 7 in the right places as well as 0x10 as the header. 309 | //Technically this is outside of spec and could cause problems but its enough checks for us 310 | //to determine if the packet should be tossed or used. 311 | //This improves the speed of packet processing as well as reducing the memory overhead. 312 | //On an Isolated network this should never be a problem.... 313 | if ( messagein[1] == 0x10 && messagein[4] == 0x41 && messagein[12] == 0x37) { 314 | int addresscount = (byte) messagein[123] * 256 + (byte) messagein[124]; // number of values plus start code 315 | return addresscount -1; //Return how many values are in the packet. 316 | } 317 | return 0; 318 | } 319 | 320 | 321 | 322 | void initTest() //runs at board boot to make sure pixels are working 323 | { 324 | LEDS.showColor(CRGB(255, 0, 0)); //turn all pixels on red 325 | delay(1000); 326 | LEDS.showColor(CRGB(0, 255, 0)); //turn all pixels on green 327 | delay(1000); 328 | LEDS.showColor(CRGB(0, 0, 255)); //turn all pixels on blue 329 | delay(1000); 330 | LEDS.showColor(CRGB(0, 0, 0)); //turn all pixels off 331 | } 332 | 333 | void loop() { 334 | //Process packets 335 | int packetSize = Udp.parsePacket(); //Read UDP packet count 336 | 337 | if(packetSize){ 338 | //Serial.println(packetSize); 339 | Udp.read(packetBuffer,ETHERNET_BUFFER); //read UDP packet 340 | 341 | 342 | int count = checkACNHeaders(packetBuffer, packetSize); 343 | if (count) { 344 | 345 | //Serial.print("packet size first "); 346 | //Serial.println(packetSize); 347 | 348 | 349 | 350 | 351 | 352 | sacnDMXReceived(packetBuffer, count); //process data function 353 | 354 | 355 | 356 | 357 | } 358 | 359 | 360 | 361 | } 362 | 363 | pixelrefresh(0); 364 | 365 | 366 | } 367 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MegaPixelController 2 | Code for the Megapixel Controller using E1.31 3 | 4 | Code: This code will allow a Teensy 3.2 Based chip function as a e1.31 controller. The base functionalitly will control up to 32 universes at speeds up to 40 FPS. 5 | 6 | Requirements: The Controller should use the octows2811 methods as explained on the PJRC webiste. The controller also uses the Wiznet 5200 chip module for ethernet connectivity. The code requires the use of the one socket library located here: 7 | 8 | https://github.com/mrrees/MegaPixel-One-Socket-Ethernet 9 | 10 | Note w5500 users will need to modify the w5100 files in order for it to work correctly and utilize the full 16k buffer. 11 | 12 | 13 | --------------------------------------------------------------------------------