Using millis more than two LEDs

I’ve read all about millis on this forum and elsewhere and tried it on a couple LEDs to see how it worked. But I’m having difficulty trying to apply it to my project. I have 14 LEDs on the outside of a pentagon and another 14 on the inside. I want to rotate outside one direction and inside other direction. I don’t know where to put my “millis” statements to get this going. I’m doing a matrix with 3 DIO from an UNO and 7 port expanders. In this program I’m only using two port expanders, one for inner, other for outer pentagon. The Loop() function of course is where all the action is. Can someone help out?

/*Name: PentCWRotMillis  Test to see if I can get simultaneity using millis
  Date: 3Jun2018
  Desc: Outer and Inner pentagons rotate. 
        I2C addr used 25, 26
  MCU:  Arduino UNO
  H/W:  MCP23017 port expanders on two Pentagon sections
  Vers: 1.1
*/
#include <Wire.h>
#include <Centipede.h>
Centipede CS;
const int pins[3] = {//9,10,11 are DIO pins for RGB transistors.  Active high turns transistor on.
  9, 10, 11 //use 9, 10, 11 because these are PWM pins on UNO
};
int x = 20;//delay between LED blink
int j;
const byte rgb[7] = {0x02, 0x04, 0x08, 0x0A, 0x06, 0x0C, 0x0E}; //02=red, 04=green, 08=blue

/*void i2cdev(int j) //function to write to the PE pin number j
  { CS.digitalWrite(j, HIGH);
  delay(10);
  CS.digitalWrite(j, LOW);
   delay(10);
  }*/
void setup() {

  Wire.begin(); // start I2C
  CS.initialize();
  //Init all 7 I2C devices (points, 2 pentagons)keep this even though only pentagon being programmed
  for (byte i = 0; i < 8; i++)
  {
    CS.portMode(i, 0x00); //
  }
  // initialize the DIO pins as outputs
  for (byte i = 0; i < 3; i++)
  { pinMode(pins[i], OUTPUT);
  }
}
void loop()
{ //Outer CW rotation followed by Inner CW rotation  All (30) red , all green, then all blue
  for (byte i = 0; i < 3 ; i++) //use i=7 for 7 colors, i=3 for 3
  { PORTB = rgb[i]; //cycle colors on pins 9,10,11

    for (int j = 80; j <= 95; j++) //device 25
    {
      CS.digitalWrite(j, HIGH);
      delay(x);
      CS.digitalWrite(j, LOW);
      delay(x);}
      
//I want this to rotate the opposite way, simultaneous with one above.
      for (int j = 111; j >=96 ; j--) //Device 26
     { CS.digitalWrite(j, HIGH);
      delay(x);
      CS.digitalWrite(j, LOW);
      delay(x);
    }
  }
}

queenidog: I don't know where to put my "millis" statements to get this going.

Think about it the opposite way then. Write out your timing statements first and then put inside each one the things that should be happening at that particular interval. Basically, start with the Blink Without Delay example and then just replace the turning of the led on or off with whatever it is that you want to have happen at some interval. If you need more than one interval going on then code more than one BWoD style timing setup. Maybe it will be easier to see what you need to do that way.

The demo Several Things at a Time has 3 LEDs and can easily be extended to many more.

In each ring of LEDs how many should be lighting at the same time?

Maybe you just need one timing interval for each ring (or maybe only a single interval for both if you want them synchronized) and an array for each group of LEDs so you can move the index along to identify which LED should (or should not) be ON.

...R

Robin2, timing is not critical. I just want one rotation CW and the other CCW.

I tried a few things as Delta_G suggested but it didn't quite work so I think it's just a placement issue, where I put the brackets and braces.

As I changed some of those locations, I ended up with such neat OTHER patterns that I saved the file as different name.

I had something going where the outer lights were lighting every 200 mS, but the inner loop was blinking all 16 LEDS in the same color as the ones rotating on the outer edge. Kept that setup.

I have printout from the "Bald Engineer" who very nicely showed how to light two LEDs, or more using millis. That's the script I'm following.

The problem with my setup, I think, is the fact that the digitalWrite writes to 14 LEDS per side, not just two. I hope I don't have to do a "millis" setup for each of these 28 LEDS.

Maybe I could use the interrupt feature of my MCP23017 port expanders? When one port is "emptied", an interrupt gets generated that triggers the next routine?

The smart thing to do would be to post some of that code and maybe someone here can help you figure out what is wrong with the braces.

Be sure you use code tags when you post it so it goes in the little scrolling window.

You already have two 'state' variables, i and j. These two variables remember where the two different animations are up to in their cycle.

Now you need to add millis() timing so that each of those can advance independently. So record the last time each one moved and then look at that time in the loop() to see if it's time to increment the relevant state variable.

Note that your true number of states might increase as each value for i and j really does two timing-related things. The LED is on for a period of time, then off for a period of time. So after i moves to the next value, the LED stays on for the period x and then then off for period x before advancing i. There's a few ways to attack this - possibly the simplest is to add another state variable which remembers which half of the state it's in. (Of course there will be 2 of these, one for i and one for j.)

Remember that the names of the variables aren't downloaded to the Arduino. So there's no penalty for using long variable names. Instead of i and j, consider using something like outerRingPosition and innerRingPosition. That makes it easier for you to keep track of what's going on.

Delta_G, code was included in my first post.

Still trying to get things going...

queenidog: Delta_G, code was included in my first post.

That's the delay code. Do you want to stick with that?

I meant post the code that didn't work and let's see what you did wrong. The code you wrote using millis. That's the code you want help with right? Why not post that code.

Or keep it a secret and work on it by yourself. I don't care. I'll help you figure out what you did wrong, but I'm not going to write code for you.

Here’s what I have so far. Inside the loop function, I have one of my LED patterns using millis followed by the other pattern (going opposite direction), not using millis…yet.

The first pattern, the millis version does not work and I have moved brackets and parentheses to many different locations putting the millis commands in or outside of the For loop. No change, does not work.

The second pattern works, but this is one that I want to change so that it works simultaneously with the first one.

/*Name: PentSIMULSlowOutFastInner Test to see if I can get simultaneity using millis
  Date: 3Jun2018
  Desc: Outer and Inner pentagons rotate.
        I2C addr used 25, 26
  MCU:  Arduino UNO
  H/W:  MCP23017 port expanders on two Pentagon sections
  Vers: 1.1
*/
#include <Wire.h>
#include <Centipede.h>
Centipede CS;
const int pins[3] = {//9,10,11 are DIO pins for RGB transistors.  Active high turns transistor on.
  9, 10, 11 //use 9, 10, 11 because these are PWM pins on UNO
};
int x = 20;//delay between LED blink
int j;
const byte rgb[7] = {0x02, 0x04, 0x08, 0x0A, 0x06, 0x0C, 0x0E}; //02=red, 04=green, 08=blue

/*void i2cdev(int j) //function to write to the PE pin number j
  { CS.digitalWrite(j, HIGH);
  delay(10);
  CS.digitalWrite(j, LOW);
   delay(10);
  }*/
unsigned long previousMillisLED25 = 0;
unsigned long previousMillisLED26 = 0;
int intervalLED25 = 100;
int intervalLED26 = 100;
boolean LED25State = false;
boolean LED26State = false;
void setup() {

  Wire.begin(); // start I2C
  CS.initialize();
  //Init all 7 I2C devices (points, 2 pentagons)keep this even though only pentagon being programmed
  for (byte i = 0; i < 8; i++)
  {
    CS.portMode(i, 0x00); //
  }
  // initialize the DIO pins as outputs
  for (byte i = 0; i < 3; i++)
  { pinMode(pins[i], OUTPUT);
  }
}
void loop()
{ unsigned long currentMillis = millis();
  { //Outer does not rotate, blinks in 3 colors
    for (byte i = 0; i < 3 ; i++) //use i=7 for 7 colors, i=3 for 3
    { PORTB = rgb[i]; //cycle colors on pins 9,10,11

      for (int j = 80; j <= 95; j++) //device 25
        if ((unsigned long)(currentMillis - previousMillisLED25) >= intervalLED25)
        { LED25State = !LED25State;

          CS.digitalWrite(j, LED25State);

          previousMillisLED25 = currentMillis;
        }
      //Inner rotates
      for (int j = 111; j >= 96 ; j--) //Device 26
      { CS.digitalWrite(j, HIGH);
        delay(x);
        CS.digitalWrite(j, LOW);
        delay(x);
      }
    }
  }
}

It won't work like that. You can't mix the two. The delay calls are still blocking, and now they block your millis code from running. It's either all delay or all not-delay. You can't mix.

Okay, I had all the variables set up for the second one, just didn't apply it. I'll try it and see.

Okay, I modified the program and it is a lot worse. If I put the “if” statement and LEDstate=!LEDstate inside the For loop of each part, I get ONE LED lighting up, LED 0. Nothing else.

If I put these statements outside of the IF statement, as shown in this code, I get all the outside lights, all 14 of them blinking in white (all RGB colors at same time). Nothing in inner pattern.

I’m sure it’s all just a matter of placing the statements in the right sequence and the parentheses and braces/brackets in the right place.

NOTE: This time to save scrolling, I only included the LOOP(), not the entire program. (Nothing else changed.)

void loop()
{ unsigned long currentMillis = millis();
  { //Outer pattern to rotate CW
    for (byte i = 0; i < 3 ; i++) //use i=7 for 7 colors, i=3 for 3
    { PORTB = rgb[i]; //cycle colors on pins 9,10,11
      if ((unsigned long)(currentMillis - previousMillisLED25) >= intervalLED25)
      { LED25State = !LED25State;
        for (int j = 80; j <= 95; j++) //device 25
        { CS.digitalWrite(j, LED25State);
          previousMillisLED25 = currentMillis;
        }
      }

      //Inner pattern to rotate CCW
      if ((unsigned long)(currentMillis - previousMillisLED25) >= intervalLED26)
      { LED26State = !LED26State;
        for (int j = 111; j >= 96 ; j--) //Device 26
        { CS.digitalWrite(j, LED26State);
          previousMillisLED26 = currentMillis;
        }
      }
    }
  }
}
LED26State = !LED26State;

That is copied straight from the example which has only one LED and only two states (on and off).

You need to count more than one state. From 111 to 96 (times 2, for off and on). Don't use for() loops.

The program I used was from Bald Engineer website and he used two LEDS (LED12 and LED13). I have 28 LEDs.

How do I light 14 (on one side, 14 on the other) sequentially without using a FOR loop?

Making an array with 28 entries would almost give me the same effect as simultaneity...light one LED on outer, then one LED on inner, then outer, then inner.

queenidog: I'm sure it's all just a matter of placing the statements in the right sequence and the parentheses and braces/brackets in the right place.

That is the essence of programming :grinning:

queenidog: How do I light 14 (on one side, 14 on the other) sequentially without using a FOR loop?

First, I would put your LEDs into an array. But instead of using a for() loop to turn them on, increment the index variable with a millis() "timer." That way you can control how quickly they turn on.

Partial/Untested code

unsigned long previousMillisIncrement = 0;
unsigned long incrementInterval = 100;
unsigned byte groupIndex = 0;
const unsigned byte maxIndex = 14; 
bool onFlag = false;
byte ledPins[maxIndex] = {}; // filled with the right pins

void loop() {
  if (onFlag && (millis() - previousMillisIncrement >= incrementInterval)) {
    previousMillisIncrement = millis();
    groupIndex++;
    if (groupIndex >= maxIndex) {
      // stop turning stuff on
      groupIndex = 0;
      onFlag = false;
    } else {
      digitalWrite(ledPins[groupIndex], HIGH);
    }
  }

Somewhere in your code, you need to set onFlag to true to make the whole thing happen. I would also "reset" prevousMillisiIncrement at the same time.

I'm giving up on millis. Too tedious, doesn't work for more than 2 LEDs.

Trying arrays and interrupts.

You are making it worse for yourself by doing that.

Gave up on millis. Solved my problem by doing what’s in code blocks. A little ugly for the arrays but it works. I can create the arrays in 3 statements, but there is no saving in space, and then it’s not that intuitive, so I will stay with the ugliness. I basically just ran the one array in reverse, so now 14 lights follow each other in sequence CW, while the other 14 rotate CCW. KISS!

SequenceArray2[0] = 0x0001;  //create this array using shift bit, function "SetArray" <<
    SequenceArray2[1] = 0x0002;
    SequenceArray2[2] = 0x0004;
    SequenceArray2[3] = 0x0008;
    SequenceArray2[4] = 0x0010;
    SequenceArray2[5] = 0x0020;
    SequenceArray2[6] = 0x0040;
    SequenceArray2[7] = 0x0080;
    SequenceArray2[8] = 0x0100;
    SequenceArray2[9] = 0x0200;
    SequenceArray2[10] = 0x0400;
    SequenceArray2[11] = 0x0800;
    SequenceArray2[12] = 0x1000;
    SequenceArray2[13] = 0x2000;
    SequenceArray2[14] = 0x4000;

queenidog: I'm giving up on millis. Too tedious, doesn't work for more than 2 LEDs.

Trying arrays and interrupts.

It DOES work for as many things as you want. It's not tedious. If you can handle reading a clock in day to day life then you can use millis. It's the same concept.

There's a huge difference between you not being able to figure it out and it not working. Your problem was that you had the for loops turning all the lights on at once. Let the loop function do the looping. The loop function needs code to decide if it is time to take an action and if so the code to take it.