Author Topic: WS2812B DMX project - Flickering LEDs - Help Needed  (Read 5207 times)

0 Members and 1 Guest are viewing this topic.

Offline paul_g_787Topic starter

  • Frequent Contributor
  • **
  • Posts: 489
  • Country: gb
Re: WS2812B DMX project - Flickering LEDs - Help Needed
« Reply #25 on: November 28, 2022, 08:43:49 pm »
Try something like this:

In startup(), fill the entire LED array with an easily recognisable pattern, say red, green, blue, yellow, magenta, cyan, white repeated as many times as fits.

Leave all the DMX processing in place, but comment out the code that updates the LED data.

Change the processing within loop() to always do the FastLED.show().

Get rid of the cli(), sei() and any delay().

At least that should show if the LED data is getting transmitted correctly. If you really wanted to convince yourself, you could say every second shift the LED colours up, wrapping the colour from the last LED back to the first, to get a "walking" pattern.

I suspect that the LEDs will work just fine.

But that does not mean that the DMX part will at the same time be working correctly. I don't know enough about DMX to say that it will be totally impossible for you to do what you want on the processor you are using, but it is certainly not going to be easy.

Why is this not a good idea? It says to do this in the FastLED documentation and also to run the data from the arduino pin to the first LED via a 200Ω resistor.

I would not connect any output pin to ground unless there was some good reason to do so, and definitely not unless the data sheet made it clear that it was safe to do so. Short circuit protection for outputs is a Good ThingTM, but the widely available Worldsemi data sheet for WS2812B does not say that. On looking now, I found another data sheet here: https://datasheet.lcsc.com/szlcsc/2107081003_XINGLIGHT-XL-3535RGBC-WS2812B_C2843786.pdf . That does sort-of say that the output current is limited.

I located the place where the FastLED documentation says to connect the far end DOUT to ground. It is preceded by this: "Some of these are just good engineering. Some are just plain voodoo.". Having never had a problem myself with WS2812B LEDs that was solved by connecting the final DOUT to ground, I know which category I would place that advice in.

Tried your suggestion, the LEDs still flicker but a lot less (probably as there are less updates).

I also tried removing the DMX code and manually created an array of values instead of picking them up from DMX and the LEDs do not flicker at all like this.

I am pretty convinced that the issue is indeed the DMX code upsetting the timing of the LEDs. I am going to try re-writing the code using the DMXSerial library that was suggested and see what happens  :-//. https://github.com/mathertel/DMXSerial

I have removed the short to ground on the last WS2812LED output pin as you suggested. Thanks for the correct info!  :-+
 

Offline paul_g_787Topic starter

  • Frequent Contributor
  • **
  • Posts: 489
  • Country: gb
Re: WS2812B DMX project - Flickering LEDs - Help Needed
« Reply #26 on: November 28, 2022, 09:08:54 pm »
OK so I have re-written the code using the DMXSerial library (https://github.com/mathertel/DMXSerial) and the code is working (and is a lot simpler now), but as before when I try and send too many updates the leds flicker.
e.g. when I change the brightness using the master dimmer (first DMX channel) or when I change the colour of many LEDs at once.

It also goes completely berserk when I try to add more than 25 LEDs.
If I add only say 3 LEDs it works OK, but I start to get flicker with more than that.

In this example I have no delays in the code. Adding a delay(100); after FastLED.show(); in loop() stops the flickering but makes the fading "steppy" as before

I think this is proving that the timing is being disturbed by having the DMX and LEDs both using Interrupts.

Where could I go from here with this?

Code: [Select]
#include <DMXSerial.h>
#include <FastLED.h>

// Constants for programme
#define errorPin 12
#define outPin 13
#define ledPin 14  //Data pin for LED strip
#define ledNum 25 //Max number of LEDs in strip
#define ledType WS2812B  //Type of LEDs
#define ledColourOrder GRB //Order of data for LEDs
CRGB ledArray[ledNum]; //Define the mode and number of LEDs

// This variable will store the start channel from the DIP switches
unsigned int dmxChannel = 0;

//This variable will store the master dimmer value
byte dmxMasterDim = 0;

// Set the Arduino pin IDs for the 9 DIP switch inputs - 1, 2, 4, 8, 16, 32, 64, 128, 256
const byte dmxDips[] = {2, 3, 4, 5, 6, 7, 8, 9, 10};

void setup() {
  // Start listening for DMX data
  DMXSerial.init(DMXReceiver);

  // set default values
  DMXSerial.write(1, 0);
  DMXSerial.write(2, 0);
  DMXSerial.write(3, 0);

  // Set each of the DIP switch inputs as an input with built in pull-up resistor
  for (byte i = 0; i < sizeof(dmxDips); i++) {
    pinMode(dmxDips[i], INPUT_PULLUP);
  }

  // Read each of the DIP switches to get the DMX channel ID
  for (byte i = 0; i < sizeof(dmxDips); i++) {
    dmxChannel = dmxChannel + (!digitalRead(dmxDips[i]) << i);
  }

  // We cannot have DMX channel 0 so default to channel 1 in this case
  if (dmxChannel == 0) { dmxChannel = 1; }

  // enable outputs
  pinMode(outPin, OUTPUT); // sets the digital pin as output
  pinMode(errorPin, OUTPUT); // sets the digital pin as output
 
  //DmxAddress = 1; //Override DIP switch for testing purposes
 
  //Initialise FastLED
  FastLED.addLeds<ledType, ledPin, ledColourOrder>(ledArray, ledNum).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness( 64 ); //Set initial brightness to 0
  FastLED.clear(); //Clear all the LEDs of colour data to ensure they are all off at boot up
  //fill_solid(ledArray, ledNum, CRGB(255,255,255));  // fill white for testing purposes
  FastLED.show(); //Update the LEDs

}


void loop() {
  // Calculate how long no data packet was received
  unsigned long lastPacket = DMXSerial.noDataSince();

  // If the last packet was received within the last 3 seconds
  if (lastPacket < 3000) {
    //Variable to determine updating LEDs
    bool changed = false;
   
    //  Blank the error LED
    digitalWrite(errorPin, LOW);

    //  LED to indicate the output status of the strip
    if(DMXSerial.read(dmxChannel > 0)) { digitalWrite(outPin, HIGH); } else { digitalWrite(outPin, LOW); }

    //  Master dimmer
    if(dmxMasterDim != DMXSerial.read(dmxChannel)) {
     dmxMasterDim = DMXSerial.read(dmxChannel); //Store the new value
     FastLED.setBrightness( dmxMasterDim ); //Set the LED strip brightness
     changed = true; //Something has changed
    }

    int ledID = 0; //  LED ID number
    int maxChan = ledNum * 3 + 1; //Calculate the max DMX channel base on the number of LEDs defined
    //  Loop through the DMX channels in groups of 3 starting at channel 2
   
    for ( int i = dmxChannel + 1; i <= maxChan; i = i + 3 ) {
      //Get the LED RGB Values from the array
      int ledValR = ledArray[ledID][0];
      int ledValG = ledArray[ledID][1];
      int ledValB = ledArray[ledID][2];

      //Get the DMX RGB values
      int dmxValR = DMXSerial.read(i);
      int dmxValG = DMXSerial.read(i + 1);
      int dmxValB = DMXSerial.read(i + 2);

      //If DMX values do not match the led array values
      if ( ledValR != dmxValR || ledValG != dmxValG || ledValB != dmxValB ) {
        ledArray[ledID] = CRGB(dmxValR,dmxValG,dmxValB); //Update the led array
        changed = true; //Something has changed
      }
      ledID = ledID + 1; 
    }

    //  Update the LEDs
    if ( changed == true ) { FastLED.show(); }
   
  } else {
    // when no data was received since 3 seconds or more
    digitalWrite(errorPin, HIGH); // light up the error LED
  }
}
« Last Edit: November 28, 2022, 09:12:43 pm by paul_g_787 »
 

Offline ozcar

  • Frequent Contributor
  • **
  • Posts: 322
  • Country: au
Re: WS2812B DMX project - Flickering LEDs - Help Needed
« Reply #27 on: November 29, 2022, 10:36:41 am »
My suggestion was to start with a static LED pattern, leave the DMX code in place (but crippled so that it does does not alter the LED array data or change the brightness), and then do the FastLED.show() every time in the loop() processing. Of course, the LEDs should retain the data, so from that point of view, it would only necessary to do the FastLED.show() once, but for testing, doing it repeatedly might give evidence of some problem that does not occur all the time. The pattern should remain static, if it doesn't, then something is not right and best to find out what that is. Perhaps the DMX ISR could be overwriting something, or maybe the wiring to the first LED is too long or a bit dodgy, or ...

The FastLED code does not make use of interrupts. If the DMX interrupt processing took too long, then the ISR might get immediately re-entered when it exits, and that could stop the loop() processing from running at all (not likely from a quick look at what you do in the ISR). It is hard to see how the DMX processing could alter timing of the outputting of LED data in such a way that would cause flickering.

You said that pausing listening for DMX for a short period would be acceptable. How long is that "short period"? With the 170 LEDs that you originally mentioned, DMX receive would be locked out for around 5ms every time that you have to update the LEDs. If that is too long to be DMX-deaf, maybe the LEDs could be split into multiple strings so that the time that interrupts have to be disabled could be reduced.

It might be possible to write your own LED transmitting code, and manage to interweave code that checks for and stashes USART data at the same time, but don't expect that to be easy.
 

Offline paul_g_787Topic starter

  • Frequent Contributor
  • **
  • Posts: 489
  • Country: gb
Re: WS2812B DMX project - Flickering LEDs - Help Needed
« Reply #28 on: November 29, 2022, 07:03:22 pm »
My suggestion was to start with a static LED pattern, leave the DMX code in place (but crippled so that it does does not alter the LED array data or change the brightness), and then do the FastLED.show() every time in the loop() processing. Of course, the LEDs should retain the data, so from that point of view, it would only necessary to do the FastLED.show() once, but for testing, doing it repeatedly might give evidence of some problem that does not occur all the time. The pattern should remain static, if it doesn't, then something is not right and best to find out what that is. Perhaps the DMX ISR could be overwriting something, or maybe the wiring to the first LED is too long or a bit dodgy, or ...

Just tried as you said. I populated the array manually at the beginning of startup() with a known pattern. I also commented out the code which updates the array from the dmx data and I maded FastLED.show() run unconditionally at the end of loop().

The result is the LEDs update just fine and show my pattern fine and there is no flickering or anything like that.

Also checked the wiring, my connections are good. The data wire to the first LED from the arduino is 3cm long so doubt that us an issue.
The arduino is powered from my PC USB port and the LED strip from my bench supply. The ground from the USB is connected to the ground from my bench supply. There is a 1000µF capacitor across the LED strip power input also.

The FastLED code does not make use of interrupts. If the DMX interrupt processing took too long, then the ISR might get immediately re-entered when it exits, and that could stop the loop() processing from running at all (not likely from a quick look at what you do in the ISR). It is hard to see how the DMX processing could alter timing of the outputting of LED data in such a way that would cause flickering.

You said that pausing listening for DMX for a short period would be acceptable. How long is that "short period"? With the 170 LEDs that you originally mentioned, DMX receive would be locked out for around 5ms every time that you have to update the LEDs. If that is too long to be DMX-deaf, maybe the LEDs could be split into multiple strings so that the time that interrupts have to be disabled could be reduced.
5ms would not be a problem at all, anything up to about 50ms is not noticable to the eye in my testing.

It might be possible to write your own LED transmitting code, and manage to interweave code that checks for and stashes USART data at the same time, but don't expect that to be easy.

I don't really understand what you mean or how I would do this.


Perhaps it is the method that FadtLED.show() is using to update the LEDs combined with the sheer number of updates from DMX?
How does FastLED.show work?  Is it pumping out every LED's data each time from start to finish?
If so, is there a way to send an update to an individual LED instead of having to update all of them?

Or maybe the method I am using the parse the DMX data and update the array is losing too many CPU cycles and therefore time? Maybe there is a more efficient method

Or maybe the 328p-pu is running out of RAM?

Perhaps the data is being send to the LEDs before it has finished updating from the last time?
« Last Edit: November 29, 2022, 07:06:27 pm by paul_g_787 »
 

Offline ozcar

  • Frequent Contributor
  • **
  • Posts: 322
  • Country: au
Re: WS2812B DMX project - Flickering LEDs - Help Needed
« Reply #29 on: November 30, 2022, 01:42:21 am »
I located the FastLED code that would be used for your ATMEGA328P - it is in clockless_trinket.h (with a name like that, who would have guessed?). No real surprise to find that it uses "bitbanging".

For WS2812B LEDs, "0" or "1" bits are represented by different length pulses, that have to be transmitted at a rate of 800k bits per second. Conceptually, you would bitbang a single bit like this:

Code: [Select]
  bit = next_bit_to_transmit;
  if ( bit == 0 )
    {
      bittime = 375;               // duration of "0" bit
    } 
  else
    {
      bittime = 875;               // duration of "1" bit
    }   
  digitalWrite(Led_Pin,HIGH);      // start of bit pulse
  delayNanoseconds(bittime);       // duration of bit pulse
  digitalWrite(Led_Pin,LOW);       // end of bit pulse
  delayNanoseconds(1250-bittime);  // up to next bit slot (1250ns for 800k bit per second LED)

You would have to repeat that 24 times (8 bits each for three colours) for each LED in the string. After the data for all the LEDs has been transmitted, there has to be a "gap" (absence of pulses), for at least 50μs (not all the datasheets agree on the exact figure), which causes the LEDs to latch and use the data received.

Maybe that looks easy? The WS2812B LEDs require sub-microsecond pulse timing, hence the use of nanoseconds above. I don't think you mentioned it, but I would guess that your processor is running at 16MHz, or perhaps 20MHz, meaning it has a cycle time of 62.5ns or 50ns. Some machine instructions take only one clock cycle, others take two or three, and the code as shown there has to be achieved using at most 20 or 25 single-cycle machine instructions. That means  digitalWrite() is far too slow to use like that, and unfortunately there is actually no delayNanoseconds() for you to use. To get down to those sort of timings as required there you have to be aware of and control the exact sequence of machine instructions used.

You may struggle to even find the code that does that in clockless_trinket.h (I know I did, in the end I compiled FastLED and then disassembled the result).

Some types of pixel LEDs have clock and data lines, with the clock being provided by the controller. In that situation, it may be possible to slow down or even freeze the clock for a while, but with the WS2812B, there is no clock line, and the timing is determined by the LEDs. There is some tolerance, but you cannot slow the transmission down by much. Once you start the transmission, you have to keep going until the data for all the LEDs has been sent, so interrupts have to be disabled for all that time.

I still think it might be possible to bitbang the LED pulses and at the same time capture DMX data. However, if you can afford to turn a blind eye (deaf ear?) to DMX for 5ms, then you don't need to do that or worry about what is going on in FastLED.

I took your code as given a few days ago, with the DMX ISR. I changed the number of LEDs to 150 (just because I happen to have a 150-LED strip), and added code to the startup() to fill the LED array with a repeated 7 colour rainbow. I had to get rid of the normal ISR to stop it from complaining about there being multiple definitions for the ISR - I guess you had to do something similar. Also made loop() call FastLED.show( ) every time through. I loaded it into a 16MHz Arduino Nano board that I happen to have lying around here. On running that, I get a non-flicking rainbow down the strip.

I don't have anything to hand to generate valid DMX data, but I realised that it is easy to get the ISR to be entered, by just periodically generating a 4μs pretend start bit. So that I could be 100% sure that the ISR was actually being called, I added code at the start of the ISR to toggle one of the pins.

The image shows the result. The top trace is the LED output data, a continuous burst for 4.5ms, as expected for 150 LEDs. The bottom trace is the pin toggled on entry to the ISR (the 4μs "start bits" were 200μs apart). So, you can see that the ISR does get called, but that activity stops while the LED transmission is happening, due to interrupts being disabled for all that time. What you can't see there, is that the LED strip was not flickering at all - still rock steady.

If you can't replicate that, then your DMX processing must be messing something up, or maybe you do have a wiring problem (I can easily get the LEDs to flicker by messing with the ground connection to them).

« Last Edit: November 30, 2022, 02:00:00 am by ozcar »
 

Offline ozcar

  • Frequent Contributor
  • **
  • Posts: 322
  • Country: au
Re: WS2812B DMX project - Flickering LEDs - Help Needed
« Reply #30 on: November 30, 2022, 02:41:26 am »
I tried being a bit more destructive.

I gradually turned down the time between the 4μs "start bits", and bang, when that got to 38.5μs (so total time then less than it takes to transmit 1 start bit, 8 data bits and 2 stop bits), most of the LEDs went out. The bursts of LED data were still happening but for as far as I checked, all the bits are zero. Increasing the pseudo DMX  timing again did not restore the LED pattern. I did not try to find what happened at that point, perhaps your code overran the DMX data array.

Edit:

So, I found what was causing that.

I had left the code to update the LEDs from the DMX data in place. There was me thinking that this would not make any difference, as I was not going to go to the effort of creating valid DMX data. However it seems all too easy for total rubbish to be accepted and acted on.

I commented out the code in action_loop() that might update the LEDs from the DMX data, and now regardless of how I change the pulse timing, the LEDs do not go out or flicker.

BTW, it looks like the code in action_loop() that loops through updating the LED array will go beyond the end of the LED array if Led_Num <= 170.
« Last Edit: November 30, 2022, 07:09:36 pm by ozcar »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf