'Flickering' LED array

Ok, so for my very first ever programming project, I put together a Garage Parking Sensor. Here is a list of parts I am using:

Nano
HC-SR04 Utrasonic Sensor
WS2812B 2X2 LED Panel
a 9v+ 1A power supply (it reads nearly 11v with no load)
a light sensor (which has analog and digital output)

and here is my code:

// libraries to include
#include <RunningMedian.h>          //RunningMedian library
#include <Adafruit_NeoPixel.h>      //Adafruit NeoPixel library

// define number of samples to include in RunningMedian
RunningMedian samples = RunningMedian(10);

// Create 'leds' object to drive LEDs and define pins
#define PIN 6                       //digital pin to control color output
#define NUMLEDS 4                   //number of LEDs on panel
Adafruit_NeoPixel leds = Adafruit_NeoPixel(NUMLEDS, PIN, NEO_GRB + NEO_KHZ800);

// define pins for echo sensor
#define trig 11
#define echo 12

// define pin for light sensor
#define light A7

long count = 0;

float m = samples.getMedian();

unsigned long previousMillis=0;

void setup()
{
  //Serial.begin(9600);

  // setup pins for ultrasonic sensor
  pinMode(trig, OUTPUT);
  pinMode(echo, INPUT); 

  // setup pin for light sensor
  pinMode(light, INPUT);

  // start led panel
  leds.begin();
  leds.show();
}

void loop()
{
  {
  int lightState = analogRead(light);
  //Serial.println(lightState);
  if (lightState >= 700)
  {
    int i;
    long duration, distance;
    for(i = 0; i < 10; i++)
    {
      digitalWrite(trig, LOW);
      delayMicroseconds(2);
      digitalWrite(trig, HIGH);
      delayMicroseconds(10);
      digitalWrite(trig, LOW);
      duration = pulseIn(echo, HIGH);
      distance = (duration/2) / 29.1;
      samples.add(distance);
      float m = samples.getMedian();
      
        if (i > 10)
        {
          continue;
        }
          ledOff();
        
        if (m > 60 && m < 100)
        {
          ledGreen();
        }
        if (m <= 60 && m > 40)
        {
          ledYellow();
          previousMillis = millis();
        }
        if (m <= 40 && m > 20)
        { 
          ledRed();
          if(millis() - previousMillis >= 30000)
          {
            ledOff();
            delay(25);
            ledWhite();
            delay(300000);
            ledOff();
            return;
          }
          else
          {
            return;
          }
          previousMillis = millis();
        }
        if (m <= 20 && m > 10)        
        {
          Flash();
          if(millis() - previousMillis >= 30000)
          {
            ledOff();
            delay(25);
            ledWhite();
            delay(300000);
            ledOff();
            return;
          }
          else
          {
            return;
          }
        }
        break;
    }
 
  }
  else
  {
    ledOff();
    delay(5000);
  }   
  }        
}

void ledRed()
{
  //set display to RED
  leds.setPixelColor(0, leds.Color(255,15,0));
  leds.setPixelColor(1, leds.Color(255,15,0));
  leds.setPixelColor(2, leds.Color(255,15,0));
  leds.setPixelColor(3, leds.Color(255,15,0));
  leds.show();
}

void ledGreen()
{
  //set display GREEN
  leds.setPixelColor(0, leds.Color(0,255,0));
  leds.setPixelColor(1, leds.Color(0,255,0));
  leds.setPixelColor(2, leds.Color(0,255,0));
  leds.setPixelColor(3, leds.Color(0,255,0));
  leds.show();
}

void ledYellow()
{
  // set display YELOW
  leds.setPixelColor(0, leds.Color(254, 193, 0));
  leds.setPixelColor(1, leds.Color(254, 193, 0));
  leds.setPixelColor(2, leds.Color(254, 193, 0));
  leds.setPixelColor(3, leds.Color(254, 193, 0));
  leds.show();
}

void Flash()
{
  // flash RED/BLUE display
  leds.setPixelColor(0, leds.Color(255,0,0));  // set display RED
  leds.setPixelColor(1, leds.Color(255,0,0));
  leds.setPixelColor(2, leds.Color(255,0,0));
  leds.setPixelColor(3, leds.Color(255,0,0));
  leds.show();
  delay(50);
  leds.setPixelColor(0, leds.Color(0,0,255));  // set display BLUE
  leds.setPixelColor(1, leds.Color(0,0,255));
  leds.setPixelColor(2, leds.Color(0,0,255));
  leds.setPixelColor(3, leds.Color(0,0,255));
  leds.show();
  delay(50); 
}

void ledOff()
{
  // set display OFF
  leds.setPixelColor(0, leds.Color(0,0,0));
  leds.setPixelColor(1, leds.Color(0,0,0));
  leds.setPixelColor(2, leds.Color(0,0,0));
  leds.setPixelColor(3, leds.Color(0,0,0));
  leds.show();
}

void ledWhite()
{
  // set display to WHITE
  leds.setPixelColor(0, leds.Color(225,205,205));
  leds.setPixelColor(1, leds.Color(225,205,205));
  leds.setPixelColor(2, leds.Color(225,205,205));
  leds.setPixelColor(3, leds.Color(225,205,205));
  leds.show();
}

I haven't received the light sensor or measured the actual distances yet (so the numbers will have to be changed), but the way this should work is when pulling into the garage, the automatic headlights will turn on and shine on the light sensor (which is checked for every 5 seconds and then enables the rest of the code), followed by GREEN (GO), YELLOW (SLOWLY), RED (STOP), FLASHING BLUE/RED (just in case we haven't stopped at RED, this will definitely grab your attention!), followed by a WHITE output for 5 minutes (as a safety/nightlite sort of thing).

I used the RunningMedian library because just sitting on my desk without moving anything, the HC-SR04 output would randomly vary as much as 10cm in either direction. I know this requires a lot more processing, but overall gives a much more accurate distance.

My first question is, I am not sure where to actually call previousMillis = millis() The code works very well the way it is, but I just want to make sure it is correct. As best as I can figure out, to make it work the way I want is that 30 seconds after the RED led is first called (or 30 seconds after the FLASH is first called, just in case we've gone too far) that the light turns white. I figured if I call it at the end of YELLOW and RED sequences that it does what I want, but it just doesn't look right to me.

Secondly, when the LEDs are called to be lit, they flicker. Is there a way to make the code run smoother to prevent this? It MIGHT be a power issue, as I am powering the LED panel off the 5v on the Nano, which in turn is powered by the 9v 1A power supply, but the flickering was not noticeable when I ran the Neo_Pixel example code OR before I added the IF statement to check for lightState.

Any help and/or feedback would be greatly appreciated!

You shouldn't mix delay() and millis().

sfgelectronics:
My first question is, I am not sure where to actually call

previousMillis = millis()

You do that when you want to re-start the timer. Often that's the first thing you do inside the 'if' statement that detects when the timer has elapsed:

    if (millis() - previousMillis >= duration_of_timer) {
        previousMillis = millis();  // Re-start the timer
    }

For slightly more consistent intervals:

    if (millis() - previousMillis >= duration_of_timer) {
        previousMillis += duration_of_timer;  // Re-start the timer
    }

That way if one iteration is slightly delayed the next iteration will wait the remainder of the interval rather than a full interval.

Suppose that this statement isn't serviced for a long time due to some delay:

    if (millis() - previousMillis >= duration_of_timer) {
        previousMillis += duration_of_timer;  // Re-start the timer
    }

Then previousMillis will not be brought fully up to date, since previousMillis+duration_of_timer is much less than the current value of millis(). That is why sometimes you will see:

    while (millis() - previousMillis >= duration_of_timer) {
        previousMillis += duration_of_timer;  // Re-start the timer
    }

You call that line every time that a duration has lapsed. So in any block that is like if(millis() - previousMillis() > sometime.

The below condition is in your for (i = 0; i < 10; i++).

        if (i > 10)

{
          continue;
        }

The maximum value for the variable i is 9, so the above if always evaluates to false and therefor redundant.

At the end of your for-loop, you have a break statement. As far as I can see, it means that the variable i never will increment because you already break in the first iteration of the loop (and each time that you enter loop() and there is light, the code inside the for-loop is only executed once).

I suggest that you add a number of Serial.println statements for debugging so you can follow what your code is doing. Show if a light was detected, show the 'reported' distance, show whatever is useful. It might reveal why your lights are flickering.

You use previousMillis += duration_of_timer; when you want the timing intervals to be stable.

Ok, I have updated this section of my code to:

if (m <= 60 && m > 40)
        {
          ledYellow();
          //previousMillis = millis();
        }
        if (m <= 40 && m > 20)
        { 
          ledRed();
          if(millis() - previousMillis >= duration_of_timer) {
            previousMillis += duration_of_timer;
            ledOff();
            delay(25);
            ledWhite();
            delay(3000);
            ledOff();
            return;
          }
          else
          {
            return;
          }
          //previousMillis = millis();
        }
        if (m <= 20 && m > 10)        
        {
          Flash();
          if(millis() - previousMillis >= duration_of_timer)
          {
            previousMillis += duration_of_timer;
            ledOff();
            delay(25);
            ledWhite();
            delay(3000);
            ledOff();
            return;
          }
          else
          {
            return;
          }
        }
        //break;
    }

I also cut down on the number of samples to 5, as the HC-SR04 seems a lot more accurate now then when I first checked it out.

The problem I am having now is that instead of ledRed() or Flash() executing the way I want, it just goes straight to ledWhite (I cut the delay down to 3000 just for testing purposes). I guess I am just really confused as to actually setting the previousMillis, as this unit could get triggered multiple times a day, or only a couple times a week. The way it is working now, it seems that it just skips the ledRed and goes straight to ledWhite, same with Flash. I extended the duration_of_timer back to 30000, and it only works sometimes, and it is no where near 30 seconds. What did I do wrong?

FYI, I believe I found the problem with the flicker. The wire I had soldered to the power supply jack is cheap and shitty, and from moving everything around, the ground wire broke off. So yes, it was just running straight off of the USB for power, which I know isn't enough current. But now that you guys have identified problems with my code, I just really want to learn how to do it right.

EDIT: I guess what I am asking is where does previousMillis initially get defined then? I currently have it before void setup() as unsigned long previousMillis=0;
And as am testing this, when it does call ledWhite(), it doesn't lock onto it for the full time like it should....

The very first time, previousMillis is zero and therefore the if condition will more than likely be satisfied and if-block executed immediately.

I've update the 'red' condition below that might do the trick; marked with /**/

  if (m <= 40 && m > 20)
  {
    ledRed();
    /**/
    if (previousMillis == 0)
      previousMillis = millis();
    /**/
    if (millis() - previousMillis >= duration_of_timer)
    {
      previousMillis += duration_of_timer;
      ledOff();
      delay(25);
      ledWhite();
      delay(3000);
      ledOff();
      /**/
      previousMillis = 0;
      /**/
      return;
    }
    else
    {
      return;
    }

After ledRed(), the code checks if previousMillis is zero, If so, it sets it to millis(). If not that step is skipped.
Next previousMillis is compared against current millis. If the condition is satisfied, your ledWhite part is done and at the end previousMillis is reset to zero so it can be used for a new timing.

With your setup, you might need a few different previousMillis; e.g. previousMillisWhite for the timing of the white led, previousMillisFlashing for the flashing condition etc. It's a bit difficult to judge from your code.

I think you also have a serious flaw in your thinking; what happens if the red condition is true, the white light goes on and the driver keeps on driving forward? It will not go to the 'flash' condition during the time that the white light is on.

You might want to consider the use of a small statemachine using switch/case and dedicated functions for the different states that your code can be in.

As I see it you have 6 different states in your code

  1. Idle, basically waiting for the light
  2. Go, while distance is 'far'
  3. Slow, while distance is close
  4. Stop, if close enough
  5. Flashing, if too close
  6. White

The switch/case makes it easier to read, maintain and expand. The use of functions keeps the loop() tidy.

I just want to thank everyone for your advice. I found that my very first call to to LED panel was to set it to OFF, and this happened every iteration in my void loop(). This is what was causing the flickering.

As far as the timers, it took me a while to realize it, but I ended up implementing two separate timers. The unit will ALWAYS run the sequence from GREEN->YELLOW->RED->WHITE (or FLASH->WHITE), unless someone or something gets in the way of the sensor (in which case the unit probably isn't even needed) I feel I did have to start timer1 the last time that 'yellow' runs, and timer2 starts the last time 'red' runs (unless timer 1 times out). If it never makes it to 'RED', the output will cease when the light sensor is no longer active. This is exactly what I had envisioned when I started this project.