Using millis with a Neopixel strip

Alternative subject : “It works, how do I fix it?” :slight_smile:

This is my first time working with millis, and the code I’m posting is to work it out before adding it to the main code. The full project is an obstacle avoidance robot so it runs two DC motors and gets information from an Ultrasonic distance sensor.

As it turns out those things weren’t that hard to figure out, what has been difficult is the add on where I said to myself “Wouldn’t it be cool or funny if the Neopixel scanned back and forth like KITT from Knight Rider?”. Isn’t it always the little add-ons that make the most trouble?

So the code below does ONLY that, and uses millis for the timing. It has not been tested in conjunction with the rest of the code yet.

However the solution I’ve arrived at seems very cumbersome, maybe because I haven’t done any programming in ages.

Is this the best way, or can it be streamlined?

#include <Adafruit_NeoPixel.h>

#define LED_PIN    5
#define LED_COUNT 8

unsigned long flashInterval = 75;

int fwdState=0;
int revState=7; 

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long counter=0;

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup()
{
  Serial.begin(9600);
  
  pinMode(5, OUTPUT);
  
  strip.begin();
  strip.show();
  previousMillis = millis();
}

void loop()
{
currentMillis = millis();
if (currentMillis - previousMillis > flashInterval) {
  counter++;
  Serial.print (counter);
  if(fwdState<8){
     strip.setPixelColor(fwdState, 0, 255, 0);    
   	 strip.setPixelColor(fwdState-1, 0, 0, 0); 
     strip.show(); 
     fwdState++;
     previousMillis = currentMillis;
  }
    else {       
    
     strip.setPixelColor(revState, 0, 255, 0);  
     strip.setPixelColor(revState+1, 0, 0, 0); 
     strip.show();  
     revState--;
	previousMillis = currentMillis;
	   
  } 
  if (counter >= 16) {
    fwdState = 0;
    revState = 7;
    counter = 0;
  }
  }

}

When you write your test program, don't put it all in loop(). Put it in a separate function say.. checkLights();. then your test loop becomes..

// Test loop loop() { checkLights(); }

Now, once its working as you like, you can add it to your robot without messing up what's already working.

//robot loop void loop(void) {

checkLights();

// All the other robot things. // // }

The only other hint I can give you is that; When things start looking cumbersome, its time for tools. Are there any tools you could write that would make this easier? Possibly be helpful for your robot as well?

For example: To lessen my typing and stuff I use a timeObj tool that I wrote. It acts just like an egg timer. I set it for a duration and then go back from time to time to see if it's expired. Then I can reset it.

Also for neoPixels I have a little method that I pass in color and it just moves all the colors one pixel down the line replacing pixel 0 with the passed in color. It also returns then last pixel color. Why? So I can wrap it in another method and presto! infinite looping of a pattern around a neoPixel strip. I can use the timeObj to set how fast it goes.

Little tools like that make coding WAY easier.

Think tools!

-jim lee

Thank you, jimLee, functions! I always forgot about them!

Can you please elaborate on the tools you mentioned, or point me in a direction where I can read up on it? I did a few searches but I'm not seeing anything that seems to relate.

Well for an egg timer you'd want a class that you could

set a duration sart timing return if its done optional - start again from the last ending point.

Those 4 pieces shouldn't be all that tough one at a time. Once you put those all together in a class, you can spawn as many timers as you like.

As for rolling a pattern over a set of neoPixels

First you'd want to be able to pass in a color to start with. Next you should already know how to read a color from a pixel index. And you should already know how to set a color to a pixel index..

Use a for loop to start at the end of the string, save off the last pixel color, then looping though pull the one before over to the one your on.

when the loop is finished you've moved all the pixels over by one.

pixle 0 & pixel 1 will be the same.

grab the pixel color you passed in and save it to pixel 0. This completes the pattern.

optional - return the color that was at the end so later you can pass it back around too the beginning.

The first one, the timer is a description of a class. The second one is a description of a function. All in marketing code. Your going to have to take a shot at fleshing them out.

-jim lee

It looks like your code is incorrect because when fwdState is zero you're giving setPixelColor an index of -1, it also goes out of bounds when revState is 7.

You could streamline things if you had one index into the pixels and a direction. Set the direction to 1 to move in the forward direction and -1 to go in the reverse direction. Proceed up or down the pixels by adding the direction to the current index.

When at the beginning and end of the pixels multiply the direction variable by -1 to start in the other direction. The only slightly tricky part is determining the adjacent pixel to turn on or off, since simply adding or subtracting one to the pixel index will go out of bounds. When you light the last pixel, #7, the adjacent pixel to turn off is #6. When you light the first pixel, #0, the adjacent pixel to turn off is #1. For any other pixel just subtract the direction to the pixel index to get the pixel immediately to the left or right of it.

#define LED_COUNT 8

unsigned long flashInterval = 1000;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;

int ledIdx = 0;    // index to LED
int direction = 1;  // 1 => up, -1 => down

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

  previousMillis = millis();
}

void loop()
{
  currentMillis = millis();
  if (currentMillis - previousMillis > flashInterval) {

    previousMillis = currentMillis;

    // turn LED on
    Serial.print("LED "); Serial.print(ledIdx); Serial.print("  ON");

    // calculate ajacent LED index
    int ajacentLed = (ledIdx == (LED_COUNT - 1) ? LED_COUNT - 2 : (ledIdx == 0) ? 1 : (ledIdx - direction));

    // turn adjacent LED off 
    Serial.print(" LED ");Serial.print(ajacentLed);Serial.print("  OFF");

    Serial.println();

    // advance index
    ledIdx += direction;

    // change direction if at begining or end
    if ( (ledIdx == (LED_COUNT - 1)) ||  (ledIdx == 0)  ) direction *= (-1);

  } // if

}

Serial output:

LED 0  ON LED 1  OFF
LED 1  ON LED 0  OFF
LED 2  ON LED 1  OFF
LED 3  ON LED 2  OFF
LED 4  ON LED 3  OFF
LED 5  ON LED 4  OFF
LED 6  ON LED 5  OFF
LED 7  ON LED 6  OFF
LED 6  ON LED 7  OFF
LED 5  ON LED 6  OFF
LED 4  ON LED 5  OFF
LED 3  ON LED 4  OFF
LED 2  ON LED 3  OFF
LED 1  ON LED 2  OFF
LED 0  ON LED 1  OFF
LED 1  ON LED 0  OFF
LED 2  ON LED 1  OFF
LED 3  ON LED 2  OFF

keep in mind that sending a ledstrip signal turns off interrupts momentarily, if the ret of your code doesn’t use interrupts then there is no problem, otherwise there might be.
hey what happened to the </> button here ?

void cylonLights(uint32_t cycletime, uint32_t color) {
  static uint32_t zeromoment=millis();
  ClearLeds();
  uint16_t patternlength = (LEDCOUNT-1) * 2;
  uint32_t elapsed = (millis()-zeromoment) % cycletime;
  uint16_t pix = elapsed / (cycletime/patternlength);
  if (pix>=LEDCOUNT-1) pix=patternlength-pix; // on the way back  
  strip.setPixelColor(pix,color); 
  strip.show(); 
}

void ClearLEDS() {
  for (uint16_t i=0; i<LEDCOUNT; i++) strip.setPixelColor(i,0);  
}

Anyway, that’s how i would roughly do it, if you change cycletime, then you will have to recalculate zeromoment or it will skip position. If you want to have a tail with 8 leds it’s not going to be much of one and the afterglow of the leds may anyway be enough) you start out with pix calculated and do a loop adding 1 to it everytime, and then doing it modulo like (mdofied for fading tail)

  uint16_t pix = elapsed / (cycletime/patternlength);
  uint8_t tailpix=0;
  tail++;
  while (tailpix<tail) {
    if (pix>=LEDCOUNT-1) pix=patternlength-pix; // on the way back 
    uint8_t level = ((uint16_t) tailpix * 255)/tail;
    strip.setPixelColor(pix,ColorLevel(color, level));
    pix++;
    pix=pix % patternlength;
    tailpix++;
  }   
  strip.show()

uint32_t ColorLevel(uint32_t color, uint8_t level) {
  uint32_t outcolor=0;
  for (uint8_t i=0; i<3; i++) {
    uint16_t col = color & 0xFF;
    color >> 8;
    outcolor >> 8;
    col = (col * level)/255;
    outcolor = outcolor | (uint32_t) col << 16;
  }
  return outcolor;
}

Karma to you for the epic question of recreating night rider lights!!!

sorry i dont have lights to test but this code give you kit’s light pattern:)

i put the red color values in an array called “kit” and print them so you can see it working.
you should be able to set your light’s red channels to the array.

anyhow, to create your trail
after you got your motion going i would would keep pushing the coordinate into an array that remembers the order of the last few coordinates. then you can trail back assigning the faded values.

unsigned long flashInterval = 1000;

int fwdState=0;
int revState=7;

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
byte glow [5]={255,125,50,25,0};
byte kit [8];
int last [5];
int d,c;

void setup()
{
  Serial.begin(9600);
   pinMode(5, OUTPUT);
 d=1;
 
}

void loop()
{
currentMillis = millis();
if (currentMillis - previousMillis > flashInterval) {
   previousMillis = currentMillis;
   //create back and forth pattern;
   c+=d;if(c==7||c==0){d*=-1;}
   light(c);
}}

void light(int p){
  int b = 5;
  while(b>1){b--;
  last[b]=last[b-1];
  } last[0]=p;
  b = 5; while(b>0){b--;
  kit[last[b]]=glow[b]; }
  
   b = 0; while(b<8){
    
   // assign your red channels to match the numbers 
   // in the array here !!!!
   // b should be the number of the light
   // kit[b] is the value of your alpha channel
   
   Serial.print(String(kit[b])+",");
   b++;}
   Serial.println("");}

Thanks for the help from everyone here!

The bot's components are still scattered everywhere on the table, but preliminary tests show all components working and no noticeable delay anywhere in the system. I'll try to make a video of it, but if that doesn't happen before assembly it definitely will after everything is on board and running!

Oh how i envy those who have only 1 project on their table.

DR : This is my first "big" project, and luckily I don't have the space or components to start a new project until this one is finished :D

There are many, many other things I want to do and play with ;-)

I usually have one project at a time as well. I usually pick very challenging projects that have taken 6 month to a year to complete. my last protect ended up with around 1500 lines of code. I feel people are more impressed seeing one thing that is truely great, instead of many thing that are mildly entertaining. Just my silly way. thought i would back you up and think it's great that you are focusing on a "Big" protect. if you run into any trouble let us know!

Some projects are bigger than others, some projects are ongoing, some projects have deadlines some projects come back with issues to be solved, some things break and need urgent repair (like my alarmclock) But i got inspired to clean up and put all projects on the same table to start with, at the moment it is just 1 big pile ;-)