Robot Head LED animation help.

Hello new to the forum here. Spent a lot of time trying different things but end up with things not working right. Ive got a robot head project Ive been working on but have been struggling with the code for it. I didnt have nay programming knowledge coming into this project but have been slowly getting some stuff down. Right now I have a LED strip animation that i would like to try and create. Ive gotten close to what I want it to be but have run into a problem. Main one being is that Im currently using a delay for the strips. They are like neo pixel strips so 1 data line per strip. I have 2 different lengths. Some on the head itself and 2 lengths for antennas. Been trying to make a LED chase animation where 5 or so LED's run down the strip. Right now the code I cobbled together is getting caught up in the delay line and not running them all at the same time. I tried to get is on the millis thing to avoid the delay but was unable to make it work. So I was wondering if there was a way to code to just have 5 LEDs run down the strip every second on 3 pins being able to change the number of LEDs per pin. So say 5 LEDs run down pin 3 while 10 LEDs run down pin 10. Havent been able to track down any examples that do what Im trying to accomplish. Here is what I had but this has the issue with the delay. There is also a LED matrix that is going to be running on this arduino as well for the eyes. That has no animation just a fillrect. If I need to I can add another arduino to the mix. The mouth is on a separate arduino as well. That will hopefully be voice reactive at some point. Hopefully some of that made sense. Thanks for any advice or help! Been a fun project so far and I want to learn this stuff.

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#ifndef PSTR
 #define PSTR // Make Arduino Due happy
#endif


Adafruit_NeoMatrix left = Adafruit_NeoMatrix(8, 8, 6,
  NEO_MATRIX_TOP     + NEO_MATRIX_LEFT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  NEO_GRB            + NEO_KHZ800); //left eye

Adafruit_NeoMatrix right = Adafruit_NeoMatrix(8, 8, 5,
  NEO_MATRIX_TOP     + NEO_MATRIX_RIGHT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  NEO_GRB            + NEO_KHZ800); //right eye
  
Adafruit_NeoPixel runs = Adafruit_NeoPixel(73, 3, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel SmallAnt = Adafruit_NeoPixel(15, 10, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel TallAnt = Adafruit_NeoPixel(23, 11, NEO_GRB + NEO_KHZ800);
                                           //(numled, pin, NEO_GRB + NEO_KHZ800))

void setup() {
  // put your setup code here, to run once:

  left.begin();
  left.setBrightness(40);
  right.begin();
  right.setBrightness(40);
  runs.begin();
  TallAnt.begin();
  SmallAnt.begin();
  TallAnt.setBrightness(40);
  SmallAnt.setBrightness(40);
  runs.setBrightness(40);
}

int x = left.Color(0, 255,0); // green for eyes left and right
int x2= left.Color(0,0,255); //not needed used for testing

void loop() {
  // put your main code here, to run repeatedly:
//matrix.fillRect(1,2,3,3,x); 3x3 // testing smaller eye size
 right.fillRect(0,1,4,4,x); //4x4
 left.fillRect(0,1,4,4,x);

 
{
  right.show();
  left.show();
  
}
  chase(runs.Color(0, 255, 0)); // Green
  chase(SmallAnt.Color(0, 255, 0));
  chase(TallAnt.Color(0, 255, 0));
 
}
 
static void chase(uint32_t c) {
  for(uint16_t i=0; i<runs.numPixels()+10; i++) {
      runs.setPixelColor(i  , c); // Draw new pixel green
      runs.setPixelColor(i-10,128,0,128); // Emakes backgroud leds purpleish
      runs.show();
      delay(50);
  for(uint16_t i=0; i<TallAnt.numPixels()+5; i++) {
      TallAnt.setPixelColor(i  , c); // Draw new pixel green
      TallAnt.setPixelColor(i-5,128,0,128); // makes backgroud leds purpleish
      TallAnt.show();
      delay(50);
  for(uint16_t i=0; i<SmallAnt.numPixels()+5; i++) {
      SmallAnt.setPixelColor(i  , c); // Draw new pixel green
      SmallAnt.setPixelColor(i-5,128,0,128); // makes backgroud leds purpleish
      SmallAnt.show();
      delay(50);
  }
  }
  }
}

Here it is running with everything on a test code just to make sure it was all working.


Delays must be removed from any “for” loop, and replaced with step functions that only does one iteration of the loop.
The delay is made by only running the the loop iteration once every what ever, 50mS in your case.

This is the millis thing you have not got the grip of is what you need to get a grip of.

Post the code for that and we can point you in the right direction.

Well I tried some stuff with this but it just ends up jumping 5 green leds along the strip. Looks like it tries to start a chase animation before jumping to a new section of the strip. I feel like Im maybe close with it. Tried adjusting the interval time but that just made it jump around faster or slower. Here is what I was able to cobble together.

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#ifndef PSTR
 #define PSTR // Make Arduino Due happy
#endif
Adafruit_NeoPixel runs = Adafruit_NeoPixel(28, 3, NEO_GRB + NEO_KHZ800);
unsigned long previousMillis = 0;
const long interval = 100; 
void setup() {
  
 
 runs.begin();
  runs.setBrightness(40);
}

void loop() {
  // put your main code here, to run repeatedly:


chase(runs.Color(0, 255, 0)); // Green
}
static void chase(uint32_t c) {
  for(uint16_t i=0; i<runs.numPixels()+5; i++) {
      runs.setPixelColor(i  , c); // Draw new pixel green
      runs.setPixelColor(i-5,128,0,128); // makes backgorund color purple
      unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
      
      runs.show();
      
}
}
}

Where you are going wrong is the millis bit should be in the loop function and used to call the chase function.

Then in that function you need to remove the for structure and make the variable i a static int type of variable.the function should then do what it does then increment the variable i and check if it is less than the number of pixels you have plus 5. If so then set i back to zero. Finally call the show function.

Remove the static before the void in the function definition.

yeah Im at a total loss. I see what your saying but have no idea how to make that into code. Any examples?

Any examples

OK

This code runs the Adafruit neopixel effects found in their examples folder. I have rewritten these as a state machine. Compare what I have here to the original Adafruit examples.

At each step the push button is looked at to see if we need to change the pattern. It shows the sort of thing you have to do.

// StrandTest from AdaFruit implemented as a state machine
// pattern change by push button
// By Mike Cook Jan 2016
// Fade function added Sept 2017

#define PINforControl   4 // pin connected to the small NeoPixels strip
#define NUMPIXELS1      64 // number of LEDs on strip

#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS1, PINforControl, NEO_GRB + NEO_KHZ800);

unsigned long patternInterval = 20 ; // time between steps in the pattern
unsigned long lastUpdate = 0 ; // for millis() when last update occoured
unsigned long intervals [] = { 20, 20, 50, 100, 30 } ; // speed for each pattern - add here when adding more cases 
int fadeStep = 0; // stste variable for fade function
int numberOfCases = 4; // how many case statements or patterns you have
const byte button = 3; // pin to connect button switch to between pin and ground

void setup() {
  strip.begin(); // This initializes the NeoPixel library.
  wipe(); // wipes the LED buffers
  pinMode(button, INPUT_PULLUP); // change pattern button
}

void loop() {
  static int pattern = 4, lastReading; // start with the fade function
  int reading = digitalRead(button);
  if(lastReading == HIGH && reading == LOW){
    pattern++ ; // change pattern number
    fadeStep = 0; // reset the fade state variable
    if(pattern > numberOfCases) pattern = 0; // wrap round if too big
    patternInterval = intervals[pattern]; // set speed for this pattern
    wipe(); // clear out the buffer
    delay(50); // debounce delay
  }
  lastReading = reading; // save for next time

if(millis() - lastUpdate > patternInterval) updatePattern(pattern);
}

void  updatePattern(int pat){ // call the pattern currently being created
  switch(pat) {
    case 0:
        rainbow();
        break;
    case 1:
        rainbowCycle();
        break;
    case 2:
        theaterChaseRainbow();
        break;
    case 3:
         colorWipe(strip.Color(255, 0, 0)); // red
         break; 
         
    case 4:
         fade(0,255, 0,64, 0,0, 400); // fade from black to oraange and back
         break;                  
  } 
}

void fade(int redStartValue, int redEndValue, int greenStartValue, int greenEndValue, int blueStartValue, int blueEndValue, int totalSteps) {
static float redIncrement, greenIncrement, blueIncrement;
static float red, green, blue;
static boolean fadeUp = false;

if (fadeStep == 0){ // first step is to initialise the initial colour and increments
  red = redStartValue;
  green = greenStartValue;
  blue = blueStartValue;
  fadeUp = false;

  redIncrement = (float)(redEndValue - redStartValue) / (float)totalSteps;
  greenIncrement = (float)(greenEndValue - greenStartValue) / (float)totalSteps;
  blueIncrement = (float)(blueEndValue - blueStartValue) / (float)totalSteps;
  fadeStep = 1; // next time the function is called start the fade
}
else { // all other steps make a new colour and display it
  // make new colour
  red += redIncrement;
  green +=  greenIncrement;
  blue += blueIncrement;
 
  // set up the pixel buffer
  for (int i = 0; i < strip.numPixels(); i++) {
  strip.setPixelColor(i, strip.Color((int)red,(int)green,(int)blue));
  }
 // now display it
  strip.show();
fadeStep += 1; // go on to next step
if(fadeStep >= totalSteps) { // finished fade
  if(fadeUp){ // finished fade up and back
     fadeStep = 0;
     return; // so next call recalabrates the increments 
  }
  // now fade back
  fadeUp = true;
  redIncrement = -redIncrement;
  greenIncrement = -greenIncrement;
  blueIncrement = -blueIncrement;
  fadeStep = 1; // don't calculate the increments again but start at first change
}
 }
}

void rainbow() { // modified from Adafruit example to make it a state machine
  static uint16_t j=0;
    for(int i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
     j++;
  if(j >= 256) j=0;
  lastUpdate = millis(); // time for next change to the display
 
}
void rainbowCycle() { // modified from Adafruit example to make it a state machine
  static uint16_t j=0;
    for(int i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
  j++;
  if(j >= 256*5) j=0;
  lastUpdate = millis(); // time for next change to the display
}

void theaterChaseRainbow() { // modified from Adafruit example to make it a state machine
  static int j=0, q = 0;
  static boolean on = true;
     if(on){
            for (int i=0; i < strip.numPixels(); i=i+3) {
                strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
             }
     }
      else {
           for (int i=0; i < strip.numPixels(); i=i+3) {
               strip.setPixelColor(i+q, 0);        //turn every third pixel off
                 }
      }
     on = !on; // toggel pixelse on or off for next time
      strip.show(); // display
      q++; // update the q variable
      if(q >=3 ){ // if it overflows reset it and update the J variable
        q=0;
        j++;
        if(j >= 256) j = 0;
      }
  lastUpdate = millis(); // time for next change to the display   
}

void colorWipe(uint32_t c) { // modified from Adafruit example to make it a state machine
  static int i =0;
    strip.setPixelColor(i, c);
    strip.show();
  i++;
  if(i >= strip.numPixels()){
    i = 0;
    wipe(); // blank out strip
  }
  lastUpdate = millis(); // time for next change to the display
}


void wipe(){ // clear all LEDs
     for(int i=0;i<strip.numPixels();i++){
       strip.setPixelColor(i, strip.Color(0,0,0));
       }
}

uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

Thanks! Ive seen that example before but I don't think it had the color wipe. I was able to modify that one to get to where I was going. I only had a few minutes in the morning to experiment with it. One thing I ran into was setting the color wipe to be the first animation would make it run faster and not with the interval that was set. Might be something I did. I'll test more when I'm back home and post the code. Thanks again for the help.

One thing I ran into was setting the color wipe to be the first animation would make it run faster and not with the interval that was set

So change the first line in the loop function to:-

static int pattern = 3, lastReading; // start with the colour wipe function

The speed is controlled by this:-
unsigned long intervals [] = { 20, 20, 50, 100, 30 } ; // speed for each pattern
For the colour wipe it is element 3 which is 100mS. Remember to count from zero.

So got everything working. It seems to bug up when pressing the button over to the case 1. The animation will not play and just few leds will turn on. A cycle or two between the cases will then have the animation play no problem. Thanks again for the help! Not sure if I something wrong with the code to make the animation bug up. So here it is. Feel free to let me know if I dun goofed it up.

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>

Adafruit_NeoPixel runs = Adafruit_NeoPixel(73, 3, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel smallant = Adafruit_NeoPixel(15, 10, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel tallant = Adafruit_NeoPixel(23, 11, NEO_GRB + NEO_KHZ800);
Adafruit_NeoMatrix left = Adafruit_NeoMatrix(8, 8, 5,
  NEO_MATRIX_TOP     + NEO_MATRIX_LEFT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  NEO_GRB            + NEO_KHZ800);

Adafruit_NeoMatrix right = Adafruit_NeoMatrix(8, 8, 6,
  NEO_MATRIX_TOP     + NEO_MATRIX_RIGHT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  NEO_GRB            + NEO_KHZ800);

unsigned long patternInterval = 100 ; // time between steps in the pattern
unsigned long lastUpdate = 0 ; // for millis() when last update occoured
unsigned long intervals [] = { 50, 30 } ; // speed for each pattern - add here when adding more cases 
int fadeStep = 0; // stste variable for fade function
int numberOfCases = 2; // how many case statements or patterns you have
const byte button = 4; // pin to connect button switch to between pin and ground

void setup() {
  left.begin();
  left.setBrightness(40);
  right.begin();
  right.setBrightness(40);

  runs.begin();
  smallant.begin();
  tallant.begin();// This initializes the NeoPixel library.
  wipe(); // wipes the LED buffers
  pinMode(button, INPUT_PULLUP); // change pattern button
  runs.setBrightness(40);
  smallant.setBrightness(40);
  tallant.setBrightness(40);
}
int x = left.Color(0, 255,0);
void loop() {
  static int pattern = 0, lastReading; // start with the fade function
  int reading = digitalRead(button);
  if(lastReading == HIGH && reading == LOW){
    pattern++ ; // change pattern number
    fadeStep = 0; // reset the fade state variable
    if(pattern > numberOfCases) pattern = 0; // wrap round if too big
    patternInterval = intervals[pattern]; // set speed for this pattern
    wipe(); // clear out the buffer
    delay(100); // debounce delay
  }
  lastReading = reading; // save for next time

if(millis() - lastUpdate > patternInterval) updatePattern(pattern);
}

void  updatePattern(int pat){ // call the pattern currently being created
  switch(pat) {
    case 0:
        fade(0,255, 0,64, 0,0, 400); // fade from black to oraange and back
        break;
    case 1:
        chaseRuns(runs.Color(0, 255, 0)); //green
        chaseSmallAnt(smallant.Color(0, 255, 0));
        chaseTallAnt(smallant.Color(0, 255, 0));
        break;                  
  } 
}

void fade(int redStartValue, int redEndValue, int greenStartValue, int greenEndValue, int blueStartValue, int blueEndValue, int totalSteps) {
static float redIncrement, greenIncrement, blueIncrement;
static float red, green, blue;
static boolean fadeUp = false;

if (fadeStep == 0){ // first step is to initialise the initial colour and increments
  red = redStartValue;
  green = greenStartValue;
  blue = blueStartValue;
  fadeUp = false;

  redIncrement = (float)(redEndValue - redStartValue) / (float)totalSteps;
  greenIncrement = (float)(greenEndValue - greenStartValue) / (float)totalSteps;
  blueIncrement = (float)(blueEndValue - blueStartValue) / (float)totalSteps;
  fadeStep = 1; // next time the function is called start the fade
}
else { // all other steps make a new colour and display it
  // make new colour
  red += redIncrement;
  green +=  greenIncrement;
  blue += blueIncrement;
 
  // set up the pixel buffer
  for (int i = 0; i < runs.numPixels(); i++) {
  runs.setPixelColor(i, runs.Color((int)red,(int)green,(int)blue));
  }
 // now display it
  runs.show();
fadeStep += 1; // go on to next step
if(fadeStep >= totalSteps) { // finished fade
  if(fadeUp){ // finished fade up and back
     fadeStep = 0;
     return; // so next call recalabrates the increments 
  }
  // now fade back
  fadeUp = true;
  redIncrement = -redIncrement;
  greenIncrement = -greenIncrement;
  blueIncrement = -blueIncrement;
  fadeStep = 1; // don't calculate the increments again but start at first change
}
 }
}

void chaseRuns(uint32_t c) { // modified from Adafruit example to make it a state machine
  static int i =0;
    runs.setPixelColor(i, c);
    runs.setPixelColor(i-15,128,0,128);
    runs.show();
  i++;
  if(i >= runs.numPixels()+15){
    i = 0;
  }
  lastUpdate = millis(); // time for next change to the display
}
void chaseSmallAnt(uint32_t c) { // modified from Adafruit example to make it a state machine
  static int i =0;
    smallant.setPixelColor(i, c);
    smallant.setPixelColor(i-8,128,0,128);
    smallant.show();
  i++;
  if(i >= smallant.numPixels()+8){
    i = 0;
  }
  lastUpdate = millis(); // time for next change to the display
}
void chaseTallAnt(uint32_t c) { // modified from Adafruit example to make it a state machine
  static int i =0;
    tallant.setPixelColor(i, c);
    tallant.setPixelColor(i-10,128,0,128);
    tallant.show();
  i++;
  if(i >= tallant.numPixels()+10){
    i = 0;
  }
  lastUpdate = millis(); // time for next change to the display
}
void wipe(){ // clear all LEDs
     for(int i=0;i<runs.numPixels();i++){
       runs.setPixelColor(i, runs.Color(0,0,0));
       smallant.setPixelColor(i, smallant.Color(0,0,0));
       tallant.setPixelColor(i, smallant.Color(0,0,0));
       }{
        right.fillRect(0,1,4,4,x); //4x4
 left.fillRect(0,1,4,4,x);

 
{
  right.show();
  left.show();
}
}
}

Also here is the video of everything working with a second nano handling the voice animation.

It seems to bug up when pressing the button over to the case 1. The animation will not play and just few leds will turn on.

Yes it is this bit:-

smallant.setPixelColor(i-8,128,0,128);

and this bit:-

tallant.setPixelColor(i-10,128,0,128);

When i is zero ( or less than 8 for the first line of 10 for the second )the code is telling the libiary to use negitave pixel values. This means it will access areas of memory that could be allocated to something else, so it will screw things up.

Also this line

if(i >= smallant.numPixels()+8)

Means you are also accessing pixels beyond the maximum number allocated.