WS2812B DMX project - Flickering LEDs - Help Needed - Page 1
Forum section: electronics / projects

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 22, 2022, 06:40:59 pm
content: Hi. I am working on a DMX to WS2812B decoder project and I am encountering a problem with the leds flickering and I am stuck and need some help.The project requirement is that there is a master dimmer as the first DMX channel that dims the entire strip then 3 channels per individual WS2812B LED to control RGB supporting up to a max 170 LEDs in one DMX universe (if the DMX channel is set to 1). Then if the channel is set to a higher number simply ignore the later LEDs.I have got all of this working great but the WS2812B LEDs flicker and flash randomly. If I unplug the DMX input from my arduino, the LEDs work just fine and don't flicker.I believe that the Interrupt Service Routine is causing delays upsetting the timing of the WS2812B LEDs which causes the data to go in and out of sync.I tried stopping the ISR when updating the LEDs using a variable ISR_Disable which is set to false when the LEDs are updated but this does not appear to help the situation.This is where my knowledge of this ends and I am stuck.I am using an arduino Uno with ATMega 328P-PU.I am using the FASTLed library to control the LEDs.I am using a MAX485 to convert DMX to TTL which is connected to the arduino Serial RX pin.I am reading the DMX channel from a 9 way DIP switch (this part works fine)My code:Code:/* pinoutpin0 - serial inpin1 - serial out (unused)pin2 - DIP Switch 1 (1)pin3 - DIP Switch 2 (2)pin4 - DIP Switch 3 (4)pin5 - DIP Switch 4 (8)pin6 - DIP Switch 5 (16)pin7 - DIP Switch 6 (32)pin8 - DIP Switch 7 (64)pin9 - DIP Switch 8 (128)pin10 - DIP Switch 9 (256)pin11 - LED error1 - too much time between frames.pin12 - LED error2 - bad dmx (non dimmer data)pin13 - LED error3 - last channel not recieved.apin0 (pin14) - LED WS2812B Strip Data Pinapin1 (pin15) -apin2 (pin16) -apin3 (pin17) -apin4 (pin18) -apin5 (pin19) -pinout *///== LED STRIP ===================================================///Define LED Parameters#include <FastLED.h>#define Led_Pin 14 //Data In pin for LED strip#define Led_Num 170 //Max number of LEDs in strip#define Led_Type WS2812B //Type of LEDs#define Led_ColourOrder GRB //Order of data for LEDsCRGB Led_Arr; //Define the mode and number of LEDsbyte Led_Dimmer = 0;bool ISR_Disable = false;//Define DMX Error LED indicator pin numbers#define Pin_Error1 11#define Pin_Error2 12#define Pin_Error3 13//= DMX =========================================================///Create DMX Variablesvolatile uint8_t DmxRxField; //array of 8 bit int DMX vals (raw)volatile uint16_t DmxAddress = 0;//start address (16 bit int)volatile uint16_t DmxChannels = 511; //Max number of DMX channelsenum States {IDLE, BREAK, STARTCODE, MYADDRESSES};States gDmxState; //create a global variable.//idle - nothing interesting to be doing,//break - the break has been recieved so expect some data after a startcode.//startcode - the startcode wase recieved so we're now into the frames of data stage.//myaddresses - we've started receieving data for my addresses.//error1 is no new packet within time defined by the default timer.boolean error1 = true;int timer;int defaulterror1time=500;// i recomend at least 100 to avoid the LED flickering between packets, and not more than 2000, as 2 seconds is reasonably long.//error2 for bad DMX (non dimmer data)boolean error2 = true;//error 2 is all to do with the valid startcode.... and it only appears in that code.//error3 is for last channel not recieved.//it only is true if a new packet is recieved before the end of the last one.//its implimented in two parts.//serror3 is within the ISR, it's set to true at the begining of a packet,//serror3 is set to false once the last frame expected is recieved.boolean error3 = false;boolean serror3 = false;//in the break section of code, if it gets to a break and is still an error then that means that error3 has occoured.//= DMX INTERRUPT SERVICE ROUTINE ================================/ISR(USART_RX_vect) {//this code/function is called whenever the harware UART recieves a low startbit (well it's called after the byte is recieved.)if(ISR_Disable == false) {// two byte counter - needed to count upto all 512 channel framesstatic uint16_t DmxCounter;uint8_t USARTstate= UCSR0A; //get state before data!uint8_t DmxByte = UDR0;//get datauint8_t DmxState = gDmxState;//just load once from SRAM to increase speedif (USARTstate &(1<<FE0)) {//check for break//data has been recieved, the USART is enabled and it's low signal for more than 8 bits so an error has also been created - this is a Break.gDmxState= BREAK;//as this marks the start of a new packet we shall set a counter to the address we wish to use as our start address.DmxCounter = DmxAddress;//dmx counter counts down untill it reaches the addressif(serror3==true) {error3=true;}serror3 = true; //as we're at the start of a packet.//lets set the error1 timer back to it's default and set it to false as DMX is imcommingtimer=defaulterror1time; error1=false;}//lets assume that the break has occoured - then to get this far in the code, another frame of data has been recieved. As the break has occoured then the next frame should be the start code. The start code for DMX data for dimmer channels is the 0 value.else if (DmxState == BREAK){if (DmxByte == 0) { //normal start code detectedgDmxState= STARTCODE; error2=false;}else {//non normal start code recieved eg: RDM packet.gDmxState= IDLE; error2=true; //ignore the rest of the packet.}}//to get to this point, we've had a break, then the start code. The next frame should be the the first channel of DMX data.else if (DmxState == STARTCODE) {//now we need to see if we are in the data before the start address or at the start address.//to do this lets decrement the counter and see if it's 0.//the counter initially was set to the start address, so when it gets to the start address's frame it should be zero.if (--DmxCounter == 0) { //start address reached?//wahoo - we're at the start address so lets grab this frameDmxRxField= DmxByte;//get 1st DMX channel of deviceDmxCounter= 1;//set up counter for required channelsif(DmxChannels==1){gDmxState= IDLE;serror3 = false;}else{gDmxState= MYADDRESSES;}//}}//to get to here we've recieved a break, start code and frams upto and//including the first channel we want.else if (DmxState == MYADDRESSES) {//lets incriment the counter,//is the new counter equal to the number of channels we require?//(remember here that the DMX counter is zero indexed and the Dmx channels is 1 indexed...//if so then we've gone past our last channel so go back to the ideal state.//if not then this data is for one of our channels so chuck it into the array.DmxRxField[DmxCounter++]= DmxByte;//get channelif (DmxCounter >= DmxChannels) { //all ch received?gDmxState= IDLE;//wait for next breakserror3 = false; //as we're at the end of data expected.}}//end of ISR}}//= BOOT UP FUNCTION =============================================/void setup(){delay( 1000 ); // power-up delay///Setup DMX error LED indicator output pinspinMode(Pin_Error1,OUTPUT);pinMode(Pin_Error2,OUTPUT);pinMode(Pin_Error3,OUTPUT);// Set the pin IDs for the 9 DIP switch inputs// 1, 2, 4, 8, 16, 32, 64, 128, 256const byte Pin_Dips = {2, 3, 4, 5, 6, 7, 8, 9, 10};// Set each of the DIP switch inputs as an input with built in pull-up resistorfor (byte i = 0; i < sizeof(Pin_Dips); i++) {pinMode(Pin_Dips, INPUT_PULLUP);}//Read each of the DIP switches to get the DMX channel ID in binary//This is stored as DmxAddressfor (byte i = 0; i < sizeof(Pin_Dips); i++) {DmxAddress = DmxAddress + (!digitalRead(Pin_Dips) << i);}//DmxAddress = 10; //Override DIP switch for testing purposes//Setup LED strip// CRGB Led_Arr; //Define the mode and number of LEDs/FastLED.addLeds<Led_Type, Led_Pin, Led_ColourOrder>(Led_Arr, Led_Num).setCorrection( TypicalLEDStrip );FastLED.setBrightness( 0 ); //Set initial brightness to 0FastLED.clear(); //Clear all the LEDs of colour data to ensure they are all off at boot up// fill_solid(Led/_Arr, Led_Num, CRGB(255,255,255)); // fill white for testing purposesFastLED.show(); //Update the LEDs//Start listening for DMX dataSerial.begin(250000); //Enable serial reception with a 250k rategDmxState= IDLE; // initial statetimer=defaulterror1time;}void loop(){//lets output the error status to LED'sif(error1==true){digitalWrite(Pin_Error1, HIGH);}else{digitalWrite(Pin_Error1, LOW);} //Too much time between frames (usually cable unplugged)if(error2==true){digitalWrite(Pin_Error2, HIGH);}else{digitalWrite(Pin_Error2, LOW);} //Bad (non-DMX) data (corrupted data or RF noise from unplugged cable)//error2 is kindof untested as i dont have any unvalid or DRM dmx devicesif(error3==true){digitalWrite(Pin_Error3, HIGH);}else{digitalWrite(Pin_Error3, LOW);} //Did not receive a full DMX frame (generally lights up for desks that do not support a full DMX frame.//Check for errors and run the actions if there are none.if(error1==false & error2 == false) {action_loop();}//this is the count down part for the ''timer''if(timer==0) {error1=true;}else { timer--; }delay(10);//this delays the loop by 1ms... less than ideal but required for the timer counting down between successfull packets.//if your action function takes significant time to output stuff then you could remove the delay by making it a comment//as the action loop would be a time delay. in this case the timer is then more of a counter, with error 1 being a number//of action calls without a successfull DMX packet being recieved.}//This function does stuffvoid action_loop(){bool changed = false; //This variable will be used to determine if any DMX values have been updated since last loop//Master dimmerif(Led_Dimmer != DmxRxField) {Led_Dimmer = DmxRxField; //Store the new valueFastLED.setBrightness( DmxRxField ); //Set the LED strip brightnesschanged = true; //Update LED strip at end of loop}//Loop through DMX fields in groups of 3 starting with 1for(int i = 1; i<=511; i=i+3) {int LED = i/3; //The corresponding LED IDint VALR = Led_Arr; //Get the current LED red valueint VALG = Led_Arr; //Get the current LED green valueint VALB = Led_Arr; //Get the current LED blue valueif(VALR != DmxRxField || VALG != DmxRxField[i+1] || VALB != DmxRxField[i+2]) {Led_Arr = CRGB(DmxRxField,DmxRxField[i+1],DmxRxField[i+2]);changed = true; //Update LED strip at end of loop}}//Finally update the LEDs if any changes were made since last loopif ( changed == true) {//ISR_Disable = true;FastLED.show(); //Update the LEDschanged = false;//ISR_Disable = false;}}

author: ozcar
author_rank: Frequent Contributor
timestamp: November 22, 2022, 07:47:11 pm
content: I don't know what FASTLed does, but if it is an interrupt issue, then "disabling" the ISR with a flag tested in the ISR is not the way to go. What happens if you use cli() ... sei() instead? If that is necessary, I would expect FASTLed to do it, but who knows?

author: globoy
author_rank: Frequent Contributor
timestamp: November 22, 2022, 08:02:55 pm
content: You're right, it's most likely the UART ISR interfering with the FastLED show() method.You might do some timing analysis to see how long each DMX transmission is and often they are initiated; and also how long it takes FastLED to output its buffer to the LEDs. You'll most likely have to serialize the activities since each is so time consuming on the 328P - alternating between getting a DMX frame and then sending it to the LEDs. Maybe you can slow down the transmission of frames. At the minimum maybe get a frame and then disable the UART until you've sent it to the LEDs. You might miss some frames but you'll be assuring FastLED won't be interrupted.Years ago I did a series of DMX fixtures using the 328P. I remember that receiving DMX was pretty tricky. My code was based on Max Pierson's stuff (search for DMX on his blog: blog.wingedvictorydesign.com). He used a timer constantly firing an interrupt to determine when the Break was over. The the UART ISR tried to process incoming characters as quickly as it could. Basically a counter looking for the start of interesting data, and the end, storing the data into an array and then disabling receiving data again until the end of the next Break.

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 22, 2022, 08:08:40 pm
content: Quote from: ozcar on November 22, 2022, 07:47:11 pmFastLED is a library to control LED strips:https://github.com/FastLEDI have not heard of cli() or sei() before. Do you mean something like this:https://forum.arduino.cc/t/cli-and-sei/94102

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 22, 2022, 08:19:24 pm
content: Quote from: globoy on November 22, 2022, 08:02:55 pmWell I am glad you agree with my hypothesis, I am at least thinking on the right lines. (Coding is not my strong point, I am much better at soldering)The DMX code I have is a slightly tidied up version of this chap's code from here:https://www.crazy-logic.co.uk/projects/lighting/receiving-dmx-on-arduino-with-ardmxIt works by commenting out this code in HardwareSerial0.cppCode:#if defined(USART_RX_vect)ISR(USART_RX_vect)#elif defined(USART0_RX_vect)ISR(USART0_RX_vect)#elif defined(USART_RXC_vect)ISR(USART_RXC_vect) // ATmega8#else#error "Don't know what the Data Received vector is called for Serial"#endif{Serial._rx_complete_irq();}and replacing it with another function ISR(USART_RX_vect) { do_stuff_here; }And as far as I understand the ISR function waits for the "break" in the serial code and then reads the DMX data into an array. Then it goes idle when there is no DMX (hence why my flickering issue stops when the DMX is disconnected).I know that DMX is at 250kbps and is "idle high", it consists of a break, mark after break, then it sends out two low bits followed by 8 data bits for each channel 0-511, channel 0 is always 8 low bits.This is looped continuously over and over from the lighting desk, so pausing listening for DMX for a short period would be acceptable as it would just pick up data again on the next DMX frame.Have you got any examples of how to pause listening for DMX while sending data to the LEDs.

author: ozcar
author_rank: Frequent Contributor
timestamp: November 22, 2022, 08:25:10 pm
content: Quote from: paul_g_787 on November 22, 2022, 08:08:40 pmYes.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: November 22, 2022, 08:28:35 pm
content: Quote from: globoy on November 22, 2022, 08:02:55 pmCan't see why you'd do that when you can just use the Uart's framing error status bit to detect the break.Sounds like the UART ints are messing up the timing of the WS2812 code.Depending on how much CPU time the WS2812 code takes, you could work round this by doing the DMX receive as a foreground task instead of using interrupts. As long as you can service the UART within 44uS ( strictly 2 bytes within 88uS as the UART has at least 1 byte of buffering - long time since I used Atmel so don't recall how much buffer it has, but 1 is common on 8-bit MCUs) , which should be doable, that should work.

author: ozcar
author_rank: Frequent Contributor
timestamp: November 22, 2022, 08:48:14 pm
content: Quote from: mikeselectricstuff on November 22, 2022, 08:28:35 pmIt takes 30μs to transmit data for a single (800kHz) WS2812B LED. That is excluding the "gap" required to terminate the transmission, but that part does not require the processor's undivided attention.

author: AndyBeez
author_rank: Frequent Contributor
timestamp: November 22, 2022, 10:17:52 pm
content: I certainly agree with @ozcar that you need to issue a CLI (clear interupt enable bit) command before you call the FastLED library and issue a SEI (set interupt enable bit) command afterwards. This will force ALL interupts from jumping into the time critical FastLED routine. It will also prevent aracecondition where variables in your LED array are being changed whilst they are being read.One question regarding the RS485 transceiver: What is the RE line (read enable) connected to? Before you call FastLED, the RE line should be set HIGH (5V) to inhibit the RS485 line from receiving anymore DMX data - in theory as I don't have the data sheet here. When you have handled the new LED data, RE goes LOW (0V) to enable more DMX traffic.

author: Lunasix
author_rank: Regular Contributor
timestamp: November 22, 2022, 10:26:40 pm
content: Hi,I use many WS2812, without any problem. My boards do a lot of things over tcp/ip. Leds are connected to a spi port (clk not used), and data are sent with DMA. I don't remember exactly, but I think I use 5 spi bits for 1 led bit, thus allow having a good timing. One table is first prepared, without any timing constraint, and then the DMA does the job, at no processor cost (no interrupt), whatever number of leds you have. Easy. You just must select the spi speed to meet the timing requirements (I use mostly PIC32MZ). Interrupt is used if necessary just to start the DMA, this take only few intructions. DMA can swap between different tables.

author: AndyBeez
author_rank: Frequent Contributor
timestamp: November 22, 2022, 10:51:56 pm
content: Sadly the Mega328 does not have a DMA channel. An STM8/STM32 architecture chip would solve a lot of problems

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 23, 2022, 12:09:35 am
content: Quote from: AndyBeez on November 22, 2022, 10:17:52 pmI added cli(); at the beginning of loop and sei(); at the end of loop. This has negated most of the flickering.The first 30 or so LEDs in my strip of 60 have stopped flickering however the later LEDs in the strip still flicker and some are getting the wrong colours so the timing still appears to be off.Quote from: AndyBeez on November 22, 2022, 10:17:52 pmThe RE line is just tied to GND at the minute. Tomorrow I will add another output to the code and connect it to the RE pin of the transceiver so I can disable the transceiver at the beginning of loop and re-enable it at the end of loop and see what this does.

author: ozcar
author_rank: Frequent Contributor
timestamp: November 23, 2022, 01:33:49 am
content: Quote from: paul_g_787 on November 23, 2022, 12:09:35 amAre the colours of the first LEDs that don't flicker correct?Do you really mean that you disabled interrupts for the entire loop() processing, or only around the FastLED.show() as you were trying to do with the ISR_Disable flag?Unable to quite believe that FASTLed would not itself disable interrupts if this was required, I turned this up:https://github.com/FastLED/FastLED/wiki/Interrupt-problems.That basically says that FASTLed does indeed disable interrupts, and talks about what problems that might cause for other processing that you might be doing that is dependent on interrupts (it also confirms the 30μs per LED timing that I mentioned).Whether there is anything mentioned there that is of practical use to you I don't know, as it does somewhat say that WS2812 and AVR is not a good combination. Changing processor might require you to confront 3.3V vs 5V considerations. Raspberry Pi Pico could be a good choice.

author: Lunasix
author_rank: Regular Contributor
timestamp: November 23, 2022, 10:52:08 am
content: You can use 74LV1T04 to convert 3V3 to 5V, it works very well.

author: AndyBeez
author_rank: Frequent Contributor
timestamp: November 23, 2022, 01:32:07 pm
content: For reference, there is also a DMX serial library for the Arduino.https://github.com/mathertel/DMXSerialhttp://www.mathertel.de/Arduino/DMXSerial.aspx

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 25, 2022, 01:58:35 pm
content: OK some progress made! I added cli(); and sei(); around the entire loop function. This did not stop the flickering but adding a delay(20_; after FastLED.show(); has now stopped the flickering.Also dimming the entire strip with the first channel (master dimmer) no longer flickers at all!However the LEDs further up the strip are showing the wrong colours. So maybe the timing is still playing it up? They are showing as cyan, magenta, off (black), white. Some of them flickering.as a test pattern, I have set the desk to output the LEDs as LED0-Red, LED1-Green, LED2- Blue... and so on all the way to ch511 so they are supposed to show RED, GREEN, BLUE, RED, GREEN, BLUE.I will add a photo of the strip when my battery has finished charging on my bench PSU.This is my code now:Code:/* pinoutpin0 - serial inpin1 - serial out (unused)pin2 - DIP Switch 1 (1)pin3 - DIP Switch 2 (2)pin4 - DIP Switch 3 (4)pin5 - DIP Switch 4 (8)pin6 - DIP Switch 5 (16)pin7 - DIP Switch 6 (32)pin8 - DIP Switch 7 (64)pin9 - DIP Switch 8 (128)pin10 - DIP Switch 9 (256)pin11 - LED error1 - too much time between frames.pin12 - LED error2 - bad dmx (non dimmer data)pin13 - LED error3 - last channel not recieved.apin0 (pin14) - LED WS2812B Strip Data Pinapin1 (pin15) -apin2 (pin16) -apin3 (pin17) -apin4 (pin18) -apin5 (pin19) -pinout *///== LED STRIP ===================================================///Define LED Parameters#include <FastLED.h>#define Led_Pin 14 //Data In pin for LED strip#define Led_Num 170 //Max number of LEDs in strip#define Led_Type WS2812B //Type of LEDs#define Led_ColourOrder GRB //Order of data for LEDsCRGB Led_Arr; //Define the mode and number of LEDsbyte Led_Dimmer = 0;//Define DMX Error LED indicator pin numbers#define Pin_Error1 11#define Pin_Error2 12#define Pin_Error3 13//= DMX =========================================================///Create DMX Variablesvolatile uint8_t DmxRxField; //array of 8 bit int DMX vals (raw)volatile uint16_t DmxAddress = 0;//start address (16 bit int)volatile uint16_t DmxChannels = 511; //Max number of DMX channelsenum States {IDLE, BREAK, STARTCODE, MYADDRESSES};States gDmxState; //create a global variable.//idle - nothing interesting to be doing,//break - the break has been recieved so expect some data after a startcode.//startcode - the startcode wase recieved so we're now into the frames of data stage.//myaddresses - we've started receieving data for my addresses.//error1 is no new packet within time defined by the default timer.boolean error1 = true;int timer;int defaulterror1time=500;// i recomend at least 100 to avoid the LED flickering between packets, and not more than 2000, as 2 seconds is reasonably long.//error2 for bad DMX (non dimmer data)boolean error2 = true;//error 2 is all to do with the valid startcode.... and it only appears in that code.//error3 is for last channel not recieved.//it only is true if a new packet is recieved before the end of the last one.//its implimented in two parts.//serror3 is within the ISR, it's set to true at the begining of a packet,//serror3 is set to false once the last frame expected is recieved.boolean error3 = false;boolean serror3 = false;//in the break section of code, if it gets to a break and is still an error then that means that error3 has occoured.//= DMX INTERRUPT SERVICE ROUTINE ================================/ISR(USART_RX_vect) {//this code/function is called whenever the harware UART recieves a low startbit (well it's called after the byte is recieved.)// two byte counter - needed to count upto all 512 channel framesstatic uint16_t DmxCounter;uint8_t USARTstate= UCSR0A; //get state before data!uint8_t DmxByte = UDR0;//get datauint8_t DmxState = gDmxState;//just load once from SRAM to increase speedif (USARTstate &(1<<FE0)) {//check for break//data has been recieved, the USART is enabled and it's low signal for more than 8 bits so an error has also been created - this is a Break.gDmxState= BREAK;//as this marks the start of a new packet we shall set a counter to the address we wish to use as our start address.DmxCounter = DmxAddress;//dmx counter counts down untill it reaches the addressif(serror3==true) {error3=true;}serror3 = true; //as we're at the start of a packet.//lets set the error1 timer back to it's default and set it to false as DMX is imcommingtimer=defaulterror1time; error1=false;}//lets assume that the break has occoured - then to get this far in the code, another frame of data has been recieved. As the break has occoured then the next frame should be the start code. The start code for DMX data for dimmer channels is the 0 value.else if (DmxState == BREAK){if (DmxByte == 0) { //normal start code detectedgDmxState= STARTCODE; error2=false;}else {//non normal start code recieved eg: RDM packet.gDmxState= IDLE; error2=true; //ignore the rest of the packet.}}//to get to this point, we've had a break, then the start code. The next frame should be the the first channel of DMX data.else if (DmxState == STARTCODE) {//now we need to see if we are in the data before the start address or at the start address.//to do this lets decrement the counter and see if it's 0.//the counter initially was set to the start address, so when it gets to the start address's frame it should be zero.if (--DmxCounter == 0) { //start address reached?//wahoo - we're at the start address so lets grab this frameDmxRxField= DmxByte;//get 1st DMX channel of deviceDmxCounter= 1;//set up counter for required channelsif(DmxChannels==1){gDmxState= IDLE;serror3 = false;}else{gDmxState= MYADDRESSES;}//}}//to get to here we've recieved a break, start code and frams upto and//including the first channel we want.else if (DmxState == MYADDRESSES) {//lets incriment the counter,//is the new counter equal to the number of channels we require?//(remember here that the DMX counter is zero indexed and the Dmx channels is 1 indexed...//if so then we've gone past our last channel so go back to the ideal state.//if not then this data is for one of our channels so chuck it into the array.DmxRxField[DmxCounter++]= DmxByte;//get channelif (DmxCounter >= DmxChannels) { //all ch received?gDmxState= IDLE;//wait for next breakserror3 = false; //as we're at the end of data expected.}}//end of ISR}//= BOOT UP FUNCTION =============================================/void setup(){delay( 1000 ); // power-up delay///Setup DMX error LED indicator output pinspinMode(Pin_Error1,OUTPUT);pinMode(Pin_Error2,OUTPUT);pinMode(Pin_Error3,OUTPUT);// Set the pin IDs for the 9 DIP switch inputs// 1, 2, 4, 8, 16, 32, 64, 128, 256const byte Pin_Dips = {2, 3, 4, 5, 6, 7, 8, 9, 10};// Set each of the DIP switch inputs as an input with built in pull-up resistorfor (byte i = 0; i < sizeof(Pin_Dips); i++) {pinMode(Pin_Dips, INPUT_PULLUP);}//Read each of the DIP switches to get the DMX channel ID in binary//This is stored as DmxAddressfor (byte i = 0; i < sizeof(Pin_Dips); i++) {DmxAddress = DmxAddress + (!digitalRead(Pin_Dips) << i);}//DmxAddress = 10; //Override DIP switch for testing purposes//Setup LED strip// CRGB Led_Arr; //Define the mode and number of LEDs/FastLED.addLeds<Led_Type, Led_Pin, Led_ColourOrder>(Led_Arr, Led_Num).setCorrection( TypicalLEDStrip );FastLED.setBrightness( 0 ); //Set initial brightness to 0FastLED.clear(); //Clear all the LEDs of colour data to ensure they are all off at boot up// fill_solid(Led/_Arr, Led_Num, CRGB(255,255,255)); // fill white for testing purposesFastLED.show(); //Update the LEDs//Start listening for DMX dataSerial.begin(250000); //Enable serial reception with a 250k rategDmxState= IDLE; // initial statetimer=defaulterror1time;}void loop(){cli(); //clear interupt enable bit//lets output the error status to LED'sif(error1==true){digitalWrite(Pin_Error1, HIGH);}else{digitalWrite(Pin_Error1, LOW);} //Too much time between frames (usually cable unplugged)if(error2==true){digitalWrite(Pin_Error2, HIGH);}else{digitalWrite(Pin_Error2, LOW);} //Bad (non-DMX) data (corrupted data or RF noise from unplugged cable)//error2 is kindof untested as i dont have any unvalid or DRM dmx devicesif(error3==true){digitalWrite(Pin_Error3, HIGH);}else{digitalWrite(Pin_Error3, LOW);} //Did not receive a full DMX frame (generally lights up for desks that do not support a full DMX frame.//Check for errors and run the actions if there are none.if(error1==false & error2 == false) {action_loop();}//this is the count down part for the ''timer''if(timer==0) {error1=true;}else { timer--; }delay(1);//this delays the loop by 1ms... less than ideal but required for the timer counting down between successfull packets.//if your action function takes significant time to output stuff then you could remove the delay by making it a comment//as the action loop would be a time delay. in this case the timer is then more of a counter, with error 1 being a number//of action calls without a successfull DMX packet being recieved.sei(); //set interupt enable bit}//This function does stuffvoid action_loop(){bool changed = false; //This variable will be used to determine if any DMX values have been updated since last loop//Master dimmerif(Led_Dimmer != DmxRxField) {Led_Dimmer = DmxRxField; //Store the new valueFastLED.setBrightness( DmxRxField ); //Set the LED strip brightnesschanged = true; //Update LED strip at end of loop}//Loop through DMX fields in groups of 3 starting with 1for(int i = 1; i<=511; i=i+3) {int LED = i/3; //The corresponding LED IDint VALR = Led_Arr; //Get the current LED red valueint VALG = Led_Arr; //Get the current LED green valueint VALB = Led_Arr; //Get the current LED blue valueif(VALR != DmxRxField || VALG != DmxRxField[i+1] || VALB != DmxRxField[i+2]) {Led_Arr = CRGB(DmxRxField,DmxRxField[i+1],DmxRxField[i+2]);changed = true; //Update LED strip at end of loop}}//Finally update the LEDs if any changes were made since last loopif ( changed == true) {FastLED.show(); //Update the LEDsdelay(20);changed = false;}}

author: AndyBeez
author_rank: Frequent Contributor
timestamp: November 25, 2022, 05:50:32 pm
content: First, check your current (amps) budget. You are providing enough to drive all of the LED's? Leds are greedy little things.With ALL leds on (white state)170 leds * 3 rgb * 0.018 amps each = 9.18 Amps!With alternating R G B that's at least 3 Amps needed.Forheadroomyou should be able to drive at least 10 Amps into the chain.Measure the voltage along the chain to see if it fades away. For long strands, it is normal to "ring" the supply voltage by feeding +/- to both ends (not Data though).btw Nice to see your code is well commented

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 25, 2022, 07:16:35 pm
content: Quote from: AndyBeez on November 25, 2022, 05:50:32 pmInterestingly when I set the LEDs to all white by pressing the "full on" button on the desk they all respond and turn white but the ones that are playing up further up the chain have about a 1 second delay before they change to white.When I let go of the full on button they all revert to the Red, Green, Blue pattern but the ones at the end start misbehaving again.When they are all on full brightness all white they are drawing 1.8104A at 5.00V (60 x WS2812B). I have tried with current limiting off also, (I have a 5 amp supply) and this made no change.I measured the voltage at the far end of the strip and it is 4.992V (the strip is 1m long).I also tried connecting the power at both ends of the strips as you suggested and this made no change at all.Also the "data out" pin of the last LED is shorted to GND.I have also tried a second strip with exactly the same measurements and the same problem with the LEDs and it is identical. So I am pretty certain it is the software causing the issue.Edit: The first LED that is playing up is the 48th one in the chain. it works on Red and Green but the Blue does nothing. This LED uses DMX channels 143 (Red), 144 (Green), 145 (Blue), so it is playing up from DMX channel 145 onwards.

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 25, 2022, 08:00:42 pm
content: Ah I have discovered something.I decided to RTFM and my desk, an img StageLine DMX-1440 only supports 144 channels. so that is why channel 145 onwards is cutting out.I joined both strips together to make a 120-long LED strip and I set the desk to full on all channels so the LEDs are all white. I have noticed that the data cuts off to the LEDs at LED 85 which is channel 254, 255 and 256. Then channel 257 is on full all the time making LED 86 red.So what I believe is happening is that my desk is outputting a half-frame of DMX cutting off at channel 256, but only allows you to control up to channel 144 due to the desk being limited in memory. (it is only a cheap one we use in the foyer).Then I think what is happening is the desk is making channel 257 permanently fully on, which would give a row of 1s or "highs" which signifies the end of the DMX frame because DMX is idle high.Does anyone else agree with this?I am going to solder up the circuit on veroboard and take it to the theatre and try it on the "big desk" which supports a full 511 DMX channels.

author: AndyBeez
author_rank: Frequent Contributor
timestamp: November 25, 2022, 08:00:57 pm
content: Slighty dubious logic here. Before the loop, set LED to 0 and then LED++ inside the loop. Avoid doing divisions on integers.

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 25, 2022, 08:31:28 pm
content: Quote from: AndyBeez on November 25, 2022, 08:00:57 pmI set it as int so it rounds down the decimal result to a whole number and it is giving me the correct result. But open to better ways of doing this of course.Is there a better way of getting the LED ID number here?Just been doing some testing and if I set delay(20); to anything lower than 20ms the LEDs (up to number 48 which I can control with this desk) begin to flicker when I dim the strip (so still a timing issue same as before).This means for 170 leds I would need a 71ms delay, but if I have the delay this long make the strip jittery and it looks bad when dimming.Any ideas here?

author: themadhippy
author_rank: Super Contributor
timestamp: November 25, 2022, 08:32:40 pm
content: QuoteMight be worth while getting a basic chamsys dongle

author: ozcar
author_rank: Frequent Contributor
timestamp: November 25, 2022, 10:05:25 pm
content: I have to think that your problem is with receiving the DMX data, rather than transmitting to the LEDs, or some power issue with the LEDs.Original suggestion to disable interrupts using cli() was made not knowing that FastLED does that itself. At least that is what is said on the FastLED page that I previously gave the link to. I tried to confirm that and see exactly what FastLED does do, but the code is a bit too convoluted for me to quickly locate the code that actually outputs the LED data.However assuming that it does disable interrupts, then you inserting cli() into your code should not make a difference to the transmitting of data to the LEDs. On the other hand, the length of time that interrupts are disabled for can cause trouble because it will prevent the USART receive ISR from executing, and that could cause you to lose data. Inserting you own cli() will alter the length of time that interrupts are disabled, and could cause weird synchronisation issues, particularly if you extend the time artificially by inserting delays into the code (not to mention potential lockouts due to preventing timer ISR from running as well).Quote from: paul_g_787 on November 25, 2022, 07:16:35 pmNot a good idea.

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 27, 2022, 12:47:21 pm
content: Quote from: ozcar on November 25, 2022, 10:05:25 pmI think you are right. Perhaps an alternate method for receiving DMX? Is there a way of running a second ISR concurrently?Or perhaps a way of picking up DMX in the mail loop() function and leaving the ISR free for FastLED?Quote from: ozcar on November 25, 2022, 10:05:25 pmThere is a function called action_loop() which is called once in loop(). This function processes the DMX data and updates the LEDs.Quote from: ozcar on November 25, 2022, 10:05:25 pmPerhaps that it what is causing my delay?Quote from: ozcar on November 25, 2022, 10:05:25 pmWhy 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.

author: ozcar
author_rank: Frequent Contributor
timestamp: November 27, 2022, 10:47:08 pm
content: 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.Quote from: paul_g_787 on November 27, 2022, 12:47:21 pmI 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.

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 28, 2022, 08:43:49 pm
content: Quote from: ozcar on November 27, 2022, 10:47:08 pmTried 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/DMXSerialI have removed the short to ground on the last WS2812LED output pin as you suggested. Thanks for the correct info!

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 28, 2022, 09:08:54 pm
content: 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 beforeI 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:#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 LEDsCRGB ledArray; //Define the mode and number of LEDs// This variable will store the start channel from the DIP switchesunsigned int dmxChannel = 0;//This variable will store the master dimmer valuebyte dmxMasterDim = 0;// Set the Arduino pin IDs for the 9 DIP switch inputs - 1, 2, 4, 8, 16, 32, 64, 128, 256const byte dmxDips[] = {2, 3, 4, 5, 6, 7, 8, 9, 10};void setup() {// Start listening for DMX dataDMXSerial.init(DMXReceiver);// set default valuesDMXSerial.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 resistorfor (byte i = 0; i < sizeof(dmxDips); i++) {pinMode(dmxDips, INPUT_PULLUP);}// Read each of the DIP switches to get the DMX channel IDfor (byte i = 0; i < sizeof(dmxDips); i++) {dmxChannel = dmxChannel + (!digitalRead(dmxDips) << i);}// We cannot have DMX channel 0 so default to channel 1 in this caseif (dmxChannel == 0) { dmxChannel = 1; }// enable outputspinMode(outPin, OUTPUT); // sets the digital pin as outputpinMode(errorPin, OUTPUT); // sets the digital pin as output//DmxAddress = 1; //Override DIP switch for testing purposes//Initialise FastLEDFastLED.addLeds<ledType, ledPin, ledColourOrder>(ledArray, ledNum).setCorrection( TypicalLEDStrip );FastLED.setBrightness( 64 ); //Set initial brightness to 0FastLED.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 purposesFastLED.show(); //Update the LEDs}void loop() {// Calculate how long no data packet was receivedunsigned long lastPacket = DMXSerial.noDataSince();// If the last packet was received within the last 3 secondsif (lastPacket < 3000) {//Variable to determine updating LEDsbool changed = false;// Blank the error LEDdigitalWrite(errorPin, LOW);// LED to indicate the output status of the stripif(DMXSerial.read(dmxChannel > 0)) { digitalWrite(outPin, HIGH); } else { digitalWrite(outPin, LOW); }// Master dimmerif(dmxMasterDim != DMXSerial.read(dmxChannel)) {dmxMasterDim = DMXSerial.read(dmxChannel); //Store the new valueFastLED.setBrightness( dmxMasterDim ); //Set the LED strip brightnesschanged = true; //Something has changed}int ledID = 0; // LED ID numberint 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 2for ( int i = dmxChannel + 1; i <= maxChan; i = i + 3 ) {//Get the LED RGB Values from the arrayint ledValR = ledArray;int ledValG = ledArray;int ledValB = ledArray;//Get the DMX RGB valuesint 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 valuesif ( ledValR != dmxValR || ledValG != dmxValG || ledValB != dmxValB ) {ledArray = CRGB(dmxValR,dmxValG,dmxValB); //Update the led arraychanged = true; //Something has changed}ledID = ledID + 1;}// Update the LEDsif ( changed == true ) { FastLED.show(); }} else {// when no data was received since 3 seconds or moredigitalWrite(errorPin, HIGH); // light up the error LED}}

author: ozcar
author_rank: Frequent Contributor
timestamp: November 29, 2022, 10:36:41 am
content: 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.

author: paul_g_787
author_rank: Frequent Contributor
timestamp: November 29, 2022, 07:03:22 pm
content: Quote from: ozcar on November 29, 2022, 10:36:41 amJust 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.Quote from: ozcar on November 29, 2022, 10:36:41 am5ms would not be a problem at all, anything up to about 50ms is not noticable to the eye in my testing.Quote from: ozcar on November 29, 2022, 10:36:41 amI 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 methodOr 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?

author: ozcar
author_rank: Frequent Contributor
timestamp: November 30, 2022, 01:42:21 am
content: 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: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 pulsedelayNanoseconds(bittime); // duration of bit pulsedigitalWrite(Led_Pin,LOW); // end of bit pulsedelayNanoseconds(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).

author: ozcar
author_rank: Frequent Contributor
timestamp: November 30, 2022, 02:41:26 am
content: 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.