LEDs flickering

I have a project working decently well but there is an issue of flicker. Without getting into too much detail, I have 4 LED tube lights connected via this socket kit and dimmer to my Arduino. There is also a LIDAR sensor for input. My code is attached at the end for context.

I understand that the LEDs always flicker, but at a high enough rate we can't see the flicker. And then with PWM, dimming to low brightness will produce a lower frequency flicker that we will be able to see more easily. But I am wondering if there is a way to smooth/avoid this effect and work around it. Any ideas?

I'm really trying to reduce the flicker without having to switch my whole design/system to work with halogens instead. Not sure if it is a dead end due to dimming LEDs in the first place, or if there is something I can modify in the dimmer module or code...

I appreciate any help!

#include <RBDdimmer.h>
#include <Wire.h>
#include <LIDARLite.h>

#define USE_SERIAL  Serial
#define outputPin  10 
#define zerocross  2 // for boards with CHANGEBLE input pins

dimmerLamp dimmer(outputPin); //initialase port for dimmer for MEGA, Leonardo, UNO, Arduino M0, Arduino Zero

LIDARLite myLidarLite;

const float min_dist = 150;
const float mid_dist = 250;
const float max_dist = 400;
const float wall_dist = 850; //set too high if you want the brightness to not be stored
const float min_brightness = 40; //functionally off
const float mid_brightness = 50;
const float max_brightness = 89; //functionally brightest
const float m = -1*(max_brightness-mid_brightness)/(mid_dist-min_dist);
const float m_far = -1*(mid_brightness-min_brightness)/(max_dist-mid_dist);

float stored_brightness = min_brightness;

bool PRESENCE = false;

unsigned long startTime;
const unsigned long period = 10000;

bool timing=false;
bool periodElapsed = false;

void setup() {
  USE_SERIAL.begin(9600);

  myLidarLite.begin(0, true); // Set configuration to default and I2C to 400 kHz
  myLidarLite.configure(0); // Change this number to try out alternate configurations
  
  pinMode(outputPin, OUTPUT);
  pinMode(zerocross, INPUT);
  dimmer.begin(NORMAL_MODE, ON); //dimmer initialisation: name.begin(MODE, STATE) 
  dimmer.toggleSettings(0, 100); //Name.toggleSettings(MIN, MAX);
  dimmer.setPower(max_brightness);
  dimmer.setState(ON); // state: dimmer1.setState(ON/OFF);
}

void setDimmerOnPresence(float dist) {
  
  float brightness = m*(dist-min_dist)+max_brightness; //linear dropoff
  float far_brightness = m_far*(dist-mid_dist)+mid_brightness;
 
  dist = dist-5.0;

  if(dist>wall_dist){
    dimmer.setPower(stored_brightness);
  }
  else if(dist>max_dist){
    dimmer.setPower(min_brightness);
  }
  else{
    if(dist<min_dist){
      dimmer.setPower(max_brightness-2);
    }
    else if(dist>mid_dist){
      dimmer.setPower(far_brightness);
    }
    else{
      dimmer.setPower(brightness);
    }
  }
  stored_brightness=dimmer.getPower();
}

void setDimmerToMin() {
   dimmer.setPower(40);
//   Serial.println("No one is present and period elapsed, setting dimmer to min now");
}

bool presenceOrShortAbsence(bool someonePresentNow)
{
  if (someonePresentNow){
    // someone is present
    timing = false;
    periodElapsed = false;

    Serial.print("Someone is present now");
    Serial.print("        ");
    
    return(true);
  }
    
  // no one present
  if (periodElapsed){    // no need for timing, no one present and period already elapsed
    Serial.print("Period elapsed");
    Serial.print("        ");
    return(false);
  }
    
  // no one present and period not elapsed, check time
  if (!timing){
    // start timing now 
    startTime = millis();
    timing = true;
    return(true);
  }
  
  // timing already started
  if (millis() - startTime < period){
    // period not elapsed
    Serial.print("Timing");
    Serial.print("        ");
    return(true);
  }

  // period just elapsed      
  timing = false;
  periodElapsed = true;
  return(false);        
}

void loop() {
  float dist = myLidarLite.distance();
  
  if(dist<wall_dist){
    PRESENCE = true;
  }
  else{
    PRESENCE = false;
  }

  if(presenceOrShortAbsence(PRESENCE)){
    setDimmerOnPresence(dist);
  }
  else{
    setDimmerToMin();
  }

  USE_SERIAL.print("dist = ");
  USE_SERIAL.print(dist);
  USE_SERIAL.print("        dimmer power = ");
  USE_SERIAL.print(dimmer.getPower());
  USE_SERIAL.print("        PRESENCE = ");
  USE_SERIAL.println(PRESENCE);
}

Adding onto the OP: Interestingly, I tried this with a small halogen bulb, and it also flickers a bit at lower brightnesses. Now I wonder if the flicker has something to do with the dimmer module (its hardware, zero crossing, power, etc.).

FYI for anyone having similar issues, I removed most of the flicker by just removing most of my print statements. For some reason, serial print was causing dramatic flicker in the lights.

For some reason, serial print was causing dramatic flicker in the lights.

The reason is that a print statement takes time to do, so it is like sprinkling lots of small delays all over the place.

Here is my current code.

#include <RBDdimmer.h>
#include <Wire.h>
#include <LIDARLite.h>

#define USE_SERIAL  Serial
#define outputPin  10 
#define zerocross  2 // for boards with CHANGEBLE input pins

dimmerLamp dimmer(outputPin); //initialase port for dimmer for UNO, MEGA, Leonardo...

LIDARLite myLidarLite;

const float min_dist = 150;
const float max_dist = 800;
const float mid_dist = max_dist/1.7;
const float wall_dist = 850; //set too high if you want the brightness to not be stored
const float min_brightness = 40; //functionally off
const float mid_brightness = 50;
const float max_brightness = 89; //functionally brightest
const float m = -1*(max_brightness-mid_brightness)/(mid_dist-min_dist);
const float m_far = -1*(mid_brightness-min_brightness)/(max_dist-mid_dist);

float stored_brightness = min_brightness;

bool PRESENCE = false;

unsigned long startTime;
const unsigned long period = 10000;

bool timing=false;
bool periodElapsed = false;

int time_count=0;
float last_dist;

int sound_count=0;

void setup() {
  USE_SERIAL.begin(9600);

  myLidarLite.begin(0, true); // Set configuration to default and I2C to 400 kHz
  myLidarLite.configure(0); // Change this number to try out alternate configurations
  
  pinMode(outputPin, OUTPUT);
  pinMode(zerocross, INPUT);
  dimmer.begin(NORMAL_MODE, ON); //dimmer initialisation: name.begin(MODE, STATE) 
  dimmer.toggleSettings(0, 100); //Name.toggleSettings(MIN, MAX);
  dimmer.setPower(min_brightness);
  dimmer.setState(ON); // state: dimmer1.setState(ON/OFF);
}

void setDimmerOnPresence(float dist) {
  
  float brightness = m*(dist-min_dist)+max_brightness; //linear dropoff
  float far_brightness = m_far*(dist-mid_dist)+mid_brightness;

  dist = dist-5.0;

  if(dist>wall_dist){
    dimmer.setPower(stored_brightness);
  }
  else if(dist>max_dist){
    dimmer.setPower(min_brightness);
  }
  else{
    if(dist<min_dist){
      dimmer.setPower(max_brightness-2);
    }
    else if(dist>mid_dist){
      dimmer.setPower(far_brightness);
    }
    else{
      dimmer.setPower(brightness);
    }
  }
  stored_brightness=dimmer.getPower();
}

void setDimmerToMin() {
   dimmer.setPower(40);
}

bool presenceOrShortAbsence(bool someonePresentNow)
{
  if (someonePresentNow){
    // someone is present
    timing = false;
    periodElapsed = false;
    return(true);
  }
    
  // no one present
  if (periodElapsed){    // no need for timing, no one present and period already elapsed
    return(false);
  }
    
  // no one present and period not elapsed, check time
  if (!timing){
    // start timing now 
    startTime = millis();
    timing = true;
    return(true);
  }
  
  // timing already started
  if (millis() - startTime < period){
    // period not elapsed
    return(true);
  }

  // period just elapsed      
  timing = false;
  periodElapsed = true;
  return(false);        
}

void loop() {  
  float dist = myLidarLite.distance();
  
  if(dist<wall_dist){
    PRESENCE = true;
  }
  else{
    PRESENCE = false;
  }
  
  if((dist==1.0 || dist>999.0) && (time_count<15)){ //debouncing
    time_count=time_count+1;
    return;
  }
  time_count=0;

  if(presenceOrShortAbsence(PRESENCE)){
    setDimmerOnPresence(dist);
  }
  else{
    setDimmerToMin();
  }

  USE_SERIAL.println(dist);

  last_dist=dist;
}

I wonder if the problem is the way I am trying to implement zero crossing (I don't think I am doing it correctly). Post #9 from this thread has some alternative code for zero crossing that might be useful, but I'm not sure how they work. But it might also be the dimmer's incompatibility with the lights, or the lights themselves.

Well the dimmer does say:-

We do not recommend to use dimmer with LED,

and the LEDs say

Dimmable when used with a compatible dimmer

The dimmer also says :-

Dimming can be achieved by Pulse Skip Modulation:

Method 1 — One or more cycles (sine wave signal) are transferred to the load, while following one or several cycles are blocked.

That method will definitely cause flicker.

Method 3 — Generation of modulated full sine signal of different frequency up to few hundred hertz.

That would be the way to do it.

Method 2 — Partial transferrence of each sine wave to the load

That is called phase modulation and can cause flicker at low light level.

Have you tried just the examples that come with the with RBDdimmer.h library on an incandescent load? If that works it is probably that the LED you have will not respond to phase angle modulation properly.

What you have not posted is your wiring diagram, this is often needed to make sense of code.

Thanks for the response. Grumpy_Mike: would you be able to help me understand a bit better how to execute what is mentioned in Method 3? (Method 3 -- Generation of modulated full sine signal of different frequency up to few hundred hertz.) This might be an ignorant question, but I am fairly new.

Also, sorry for not attaching a wiring diagram. Here it is.

would you be able to help me understand a bit better how to execute what is mentioned in Method 3

It is not a beginners project but google:-
mains inverters schematics

These things normally run off 12V and put the power through a transformer to get the correct mains voltage out.

If you feed this with a variable, smaller voltage, you will get you will get a lower "mains" voltage out. There is no guarantee that this will produce dimming of the LED because we don't know the LED's internal circuitry, but if it does and it flickers, then you could change the frequency to be higher than 50/60Hz then you might stop the flickering.

Note this was a method recommended by you dimmer board not the LED's specification.

Hi,

I have a follow up not directly related to the flicker but related to this project and setup. When I moved my installation to a new room (the exhibit space) with new dimensions, I changed the wall distance variable but now there is a new issue in the moment when I step out of the laser's line of sight. I think I've narrowed down the problem to the following: the system does not retain the stored_brightness because in the process of stepping out (PRESENCE to not PRESENCE), the edge of the laser sensor seems to read distances that are farther than they really are. So instead of reading 100,100,100,500,500,500,500 it reads something like 100,200,400,450,500. Those intermediate values change the brightness of the lights before storing a stored_brightness and throw things off. Instead of just the 100 dictating a brightness which is retained when the sensor reads 500.

All that said, is there a way to ignore those intermediate values (when an object leaves the laser beam) and instead have a cleaner change in distance? I tried to ignore the current loop if a PAST_PRESENCE==true and PRESENCE==false, but there is more than 1 intermediate value

Here is my recent code for reference. Thanks!

#include <MegunoLink.h>
#include <CommandHandler.h>
#include <TCPCommandHandler.h>
#include <ArduinoTimer.h>
#include <CircularBuffer.h>
#include <EEPROMStore.h>
#include <Filter.h>
ExponentialFilter<long> ADCFilter(0, 0);

#include <RBDdimmer.h>
#include <Wire.h>
#include <LIDARLite.h>

#define USE_SERIAL  Serial
#define outputPin  10 
#define zerocross  2 // for boards with CHANGEBLE input pins

dimmerLamp dimmer(outputPin); //initialase port for dimmer for UNO, MEGA, Leonardo...

LIDARLite myLidarLite;

const float min_dist = 100;
const float max_dist = 440;
const float wall_dist = 455; //set too high if you want the brightness to not be stored
const float min_brightness = 40; //40 functionally off
const float max_brightness = 89; //functionally brightest
const float m = -1*(max_brightness-min_brightness)/(max_dist-min_dist);

float stored_brightness = 42; //initialize with some value

bool PRESENCE = false;
unsigned long startTime;
const unsigned long period = 20000;
bool timing=false;
bool periodElapsed = false;
int time_count=0;

void setup() {
  USE_SERIAL.begin(9600);

  myLidarLite.begin(0, true); // Set configuration to default and I2C to 400 kHz
  myLidarLite.configure(0); // Change this number to try out alternate configurations
  
  pinMode(outputPin, OUTPUT);
  pinMode(zerocross, INPUT);
  dimmer.begin(NORMAL_MODE, ON); //dimmer initialization: name.begin(MODE, STATE) 
  dimmer.toggleSettings(0, 100); //Name.toggleSettings(MIN, MAX);
  dimmer.setPower(min_brightness);
  dimmer.setState(ON); // state: dimmer1.setState(ON/OFF);
}

void setDimmerOnPresence(float dist) {
  
  float brightness = m*(dist-min_dist)+max_brightness; //linear dropoff

  if(!PRESENCE){
    dimmer.setPower(stored_brightness);
    USE_SERIAL.print("A");
  }
  else{
    USE_SERIAL.print("B");
    if(dist>max_dist){ //max_dist
      dimmer.setPower(66); //min_brightness
    }
    if(dist<min_dist){
      dimmer.setPower(max_brightness-2);
    }
    else{
      dimmer.setPower(brightness);
    }
  }
  stored_brightness=dimmer.getPower();
}

void loop() {  
  float dist = myLidarLite.distance();
  dist = dist-5.0;
  ADCFilter.Filter(dist);
  
  if(dist<wall_dist){
    PRESENCE = true;
  }
  else{
    PRESENCE = false;
  }
  
  if((dist==1.0 || dist>999.0) && (time_count<15)){
    time_count=time_count+1;
    return;
  }
  time_count=0;
  
  setDimmerOnPresence(dist);

  USE_SERIAL.print(dimmer.getPower());
  USE_SERIAL.print("    ");
  USE_SERIAL.println(dist);
}