Teensy 4.1 + Native Ethernet + Artnet + OctoWS2811 example, big lag after 7 universes

Hi there,

I'm currently experiencing some major lag on my LED setup. My current project specifications:


I have 4 LED strips, each 300 pixels long. Each strip is going to a separate pin on the Teensy.

With just 3 strips (Art-Net universes 0-1 to 5-140), the LEDs work really smoothly. The data comes through from Mad Mapper, and the effects show nicely. All 3 strips operate mostly fine, although very fast effects seem to drop some data. However, when I add the 4th strip (Art-Net universes 0-1 to 7-16), everything comes to a grinding halt. I'm a bit stuck on this one, and can't find much insights around after extensive searching. I'm assuming that I should be able to drive a lot more pixels over Art-Net based on the notes in the OctoWS2811 library.

Further notes:

In my circuitry, each data pin feeds into an input on the 74AHCT125 level shifter, and the output feeds into the strip. It's properly grounded and powered, and tested without Art-Net (using FastLED) just for safety.

I'm using the sample code from the OctoWS2811 library… the only difference is that I swapped out Ethernet and EthernetUdp for NativeEthernet and NativeEthernetUdp.

Code:
// Receive multiple universes via Artnet and control a strip of ws2811 leds via OctoWS2811
//
// This example may be copied under the terms of the MIT license, see the LICENSE file for details
//  https://github.com/natcl/Artnet
// 
// http://forum.pjrc.com/threads/24688-Artnet-to-OctoWS2811?p=55589&viewfull=1#post55589

#include <Artnet.h>
#include <NativeEthernet.h>
#include <NativeEthernetUdp.h>
#include <SPI.h>
#include <OctoWS2811.h>

// Ideas for improving performance with WIZ820io / WIZ850io Ethernet:
// https://forum.pjrc.com/threads/45760-E1-31-sACN-Ethernet-DMX-Performance-help-6-Universe-Limit-improvements

// OctoWS2811 settings
const int numPins = 4;
byte pinList[numPins] = {9, 10, 11, 12};
const int ledsPerStrip = 300; // change for your setup
const byte numStrips= 4; // change for your setup
DMAMEM int displayMemory[ledsPerStrip*6];
int drawingMemory[ledsPerStrip*6];
const int config = WS2811_GRB | WS2811_800kHz;
OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config, numPins, pinList);

// Artnet settings
Artnet artnet;
const int startUniverse = 0; // CHANGE FOR YOUR SETUP most software this is 1, some software send out artnet first universe as zero.
const int numberOfChannels = ledsPerStrip * numStrips * 3; // Total number of channels you want to receive (1 led = 3 channels)
byte channelBuffer[numberOfChannels]; // Combined universes into a single array

// Check if we got all universes
const int maxUniverses = numberOfChannels / 512 + ((numberOfChannels % 512) ? 1 : 0);
bool universesReceived[maxUniverses];
bool sendFrame = 1;

// Change ip and mac address for your setup
byte ip[] = {192, 168, 1, 10};
// byte broadcast[] = {192, 168, 1, 255};
byte mac[] = {0x04, 0xE9, 0xE5, 0x00, 0x69, 0xEC};

void setup()
{
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {
    // Wait for Serial
  }
  
  // artnet.setBroadcast(broadcast);
  artnet.begin(mac, ip);
  leds.begin();
  initTest();

  // this will be called for each packet received
  artnet.setArtDmxCallback(onDmxFrame);
}

void loop()
{
  // we call the read function inside the loop
  artnet.read();
}

void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data)
{
  sendFrame = 1;

  // Store which universe has got in
  if (universe < maxUniverses)
    universesReceived[universe] = 1;

  for (int i = 0 ; i < maxUniverses ; i++)
  {
    if (universesReceived[i] == 0)
    {
      // Serial.println("Broke");
      sendFrame = 0;
      break;
    }
  }

  // read universe and put into the right part of the display buffer
  for (int i = 0 ; i < length ; i++)
  {
    int bufferIndex = i + ((universe - startUniverse) * length);
    if (bufferIndex < numberOfChannels) // to verify
      channelBuffer[bufferIndex] = byte(data[i]);
  }      

  // send to leds
  for (int i = 0; i < ledsPerStrip * numStrips; i++)
  {
    leds.setPixel(i, channelBuffer[(i) * 3], channelBuffer[(i * 3) + 1], channelBuffer[(i * 3) + 2]);
  }      
  
  if (sendFrame)
  {
    leds.show();
    // Reset universeReceived to 0
    memset(universesReceived, 0, maxUniverses);
  }
}

void initTest()
{
  for (int i = 0 ; i < ledsPerStrip * numStrips ; i++)
    leds.setPixel(i, 127, 0, 0);
  leds.show();
  delay(500);
  for (int i = 0 ; i < ledsPerStrip * numStrips  ; i++)
    leds.setPixel(i, 0, 127, 0);
  leds.show();
  delay(500);
  for (int i = 0 ; i < ledsPerStrip * numStrips  ; i++)
    leds.setPixel(i, 0, 0, 127);
  leds.show();
  delay(500);
  for (int i = 0 ; i < ledsPerStrip * numStrips  ; i++)
    leds.setPixel(i, 0, 0, 0);
  leds.show();
}

And in Mad Mapper, here's my DMX settings:

Screenshot 2023-10-03 at 3.39.10 PM.jpg

Any help would be greatly appreciated.

Thank you!
 
This is not specific help on your issue, but you should try to switch to QNEthernet rather than NativeEthernet, which seems to no longer be supported.
 
Thanks for that. So, I did indeed switch to QNEthernet, but the problems persist. Is there any Ethernet specific configurations that I need to make to boost performance?
 
Not sure if this is of help for you:
I can confirm that a setup with 24 output pins running on each pin 720 LEDs is working fine on a Teensy 4.1 receiving data over ethernet.

I remember vaguely when trying out Artnet at one point that I ran into problems of not rendering the data to the pixels when there was something wrong with the data/configuration. So you have to make sure that the right amount of data / universes is sent which is configured on the Teensy. Possibly not all universes are received. Then "sendFrame"... is never happening. It could help to add some debug information in the code to see what happens there or if the code is stuck and waiting for the last universe to be received.

Very vaguely I remember that there can be a problem at the last channels of a universe because 512 is not a multiple of 3 ... I had a configuration where this was not compatible between sender and receiver... but if it is running with 3 strips correctly this should be working.
 
Ok interesting. Yeah I was wondering the same thing, if it had anything to do with that. I'll explore a bit more on that front and let you know how it goes.

On another note, which software were you using to send artnet data to the teensy? And can you tell me a bit more about your hardware setup, computer, Ethernet jack, teensy version, etc? And perhaps your code that ran on the teensy?

Thanks so much!
 
I used the ArtnetOctoWS2811 sketch from the examples. There I had to change in the artnet files to use native ethernet (which worked fine here).
Code:
#include <NativeEthernet.h>
#include <NativeEthernetUdp.h>

View attachment ArtnetOctoWS2811_V1.zip
This is my test sketch displaying some debug information.

It is a Teensy 4.1 with the Ethernet kit and a cheap Ethernet cable connected to the router. Running Windows 10 and Jinx! as software. As for a huge number of pixels (~10-20k) many universes (and therefore artnet devices) are needed, the configuration was extremely painful and I decided to switch to tpm2.net. (I haven't found a way and didn't put more work in finding a solution that the devices would be recognized automatically) The configuration in Jinx! is much easier this way having one tpm2.net device with all the channels. Mapping is also easy this way. For such numbers packet sizes begin to play a role for the performance over ethernet. 1024 gives good results in my tests.

In the future I would also like to look into Mad Mapper and try other software. In the last project Jinx! gave a sufficiently good solution. With the script language I could program the pixels quite good. Much easier than programming it all on the microcontroller. Limited frame rate of 25 was a little disadvantage. Other aspects are the workflow, who is controlling the software at the end, what effects and how much complexity are needed, what should be pre-programmed and how flexible can things be done live ... still looking for more options depending on the exact project goals.

Btw. have you checked the configuration of the universes in Madmapper? It is not shown in your screenshot. It is below the shown part.
 
For the ethernet connection I am using the ethernet kit.

To connect the LED strips I use three of the OctoWS2811 adaptors which come in quite handy using cat6 (or so) cables. I am using RJ45 splitter to distribute the data channels if needed. This is very flexible. At the end I have self made cables for the connection to the LEDs and power.
 
Ohh I see. So you are using the OctoWS2811 adaptors to actually drive the LEDs?

My current setup is that I am running ArtNet from my computer -> network switch -> teensy via ethernet adapter. Then I am just wiring the LEDs directly out from the Teensy's pins.

Here is the teensy with wiring going out to LEDs:

IMG_4929 Medium.jpeg

Network switch with cable in from laptop, and cable out to teensy ethernet adaptor:

IMG_4931 Medium.jpeg

Teensy ethernet adaptor:

IMG_4932 Medium.jpeg

And Teensy with wiring out again:

IMG_4933 Medium.jpeg

The interesting thing is that prior to the buggy threshold, the light on the Teensy flashes rapidly, like crazy. And the LEDs are smooth. Beyond the bug threshold though, which I've narrowed down to showing signs around universe 6 channel 60 (universes starting at 0, so around 1040 LEDs), the light on the Teensy no longer flashes rapidly. Just very occasionally, mimicking the overall lag factor.

So I'm curious now, is it that I need the OctoWS2811 adapter to act as the driver of the LEDs? I was initially assuming that I could just use the Teensy's default pinout, and configure it myself…
 
Run tests for the LEDs only first, i.e. some demo patterns from Teensy only, no Ethernet/Artnet whatever.

The OctoWS2811 can make the wiring easier. The 5V level shift is not a must if it is working. A lot of LED chips today work with 3.3V logic level on the data line. For longer cables it can be also a reason to shift to 5V to make this more robust. So it is right that you can use the output pins directly to connect to the LEDs (with a series resistor to protect against voltage spikes and minimize reflections in longer cables).
 
Yeah good tip, but this is also exactly why I'm a bit lost now as to the sticking point. Here's a video of the same setup running just FastLED. 8 strips, each length 300, and each assigned to a different pin. All are running the cylon effect, and all run smoothly, no data loss.

 
That's good news. Hardware running.

Now you can check the network communication. Print out debug messages to see if all packages come in or what is going on there...
 
Ok great. So first things, I slightly modified the code to only count up to 510 per universe in the loops instead of 512. In Mad Mapper, there's a setting that allows you to "skip last universe channels if needed", so a 300 length strip will occupy 0-1 to 1-390. I tested this code on just 3 strips btw, and it works fine.

So now, the code for onDmxFrame looks like this:

Code:
void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data) {
  sendFrame = 1;

  // Store which universe has got in
  if (universe < maxUniverses) {
    universesReceived[universe] = 1;
  }


  // Wait till all universes got
  for (int i = 0 ; i < maxUniverses ; i++) {
    if (universesReceived[i] == 0) {
      sendFrame = 0;
      break;
    }
  }

  // read universe and put into the right part of the display buffer
  for (int i = 0 ; i < 510 ; i++) {
    int bufferIndex = i + ((universe - startUniverse) * 510);
    if (bufferIndex < numberOfChannels) // to verify
      
      Serial.print("data:");
      Serial.println(data[i]);

      channelBuffer[bufferIndex] = byte(data[i]);
  }      

  // send to leds
  for (int i = 0; i < ledsPerStrip * numStrips; i++) {
    leds.setPixel(i, channelBuffer[(i) * 3], channelBuffer[(i * 3) + 1], channelBuffer[(i * 3) + 2]);
  }
  
  if (sendFrame) {
    leds.show();
    // Reset universeReceived to 0
    memset(universesReceived, 0, maxUniverses);
  }
}

I have logged the following:

Code:
      Serial.print("data:");
      Serial.println(data[i]);

And as predicted, I'm getting a ton of zeros here::

Code:
data:0
data:0
data:0
data:0
data:0
data:0
data:0
data:0
data:0
data:0
data:0
data:0
data:0

…

data:1
data:1
data:0
data:1
data:1
data:0
data:1
data:1
data:0
data:1
data:1
data:1

…

data:16
data:50
data:37
data:17
data:51
data:37
data:17
data:53
data:38
data:17
data:54
data:39
data:18

So for sure now there is some data loss occurring somewhere…
 
Using Teensy 4.1, OctoWS28 and the Ethernet connector, I could do 24 universes at a good quality. I throttled the Art-net server and FastLED Teensy node down to 30fps, which worked well for my project.

There seems to be a bunch of discussions and threads about the Teensy/Octo/Artnet setup, so I've put together a repo that hopefully documents everything in one place and is a consolidated boilerplate for a Teensy/Octo/Artnet/FastLED node...

The repo is here for those interested... https://github.com/jshaw/TeensyOctoWS28ArtnetNode

Using Processing for the generative visuals, this is the project I used this project template on...

I hope this helps!
 

Attachments

  • IMG_1281.jpeg
    IMG_1281.jpeg
    93.5 KB · Views: 43
Back
Top