Timing an LED Array flashing at different frequencies using millis()

Overview

I am currently working on a project that consists of an 12x8 96-LED array controlled by 8 digital PWM pins on the Arduino Mega. Each of the digital PWM pins feeds into the LED array and controls a row of LEDs, with the Arduino’s built-in PWM varying the intensity. My goal is to have each row its own object (of the class Row) with a variety of pre-defined parameters that control the frequency the LEDs flash at, as well as intensity, and other timing functions (more details below). Testing the code below, the LED boards’ behavior has suggested a timing issue. I’m hoping someone here can help me spot some errors in my code or logic to resolve these problems.

Timing

The plan is to have 8 rows of 12 LEDs each operating independently of one another, flashing at different frequencies. The LED’s flash for a predefined time (timeShort, the oscillating time) and then turn off for the remainder of the total cycle time (timeLong) after which they are meant to cycle back and repeat the cycle. After reaching timeShort, which is a percentage of timeLong, the LEDs are meant to wait for the rest of timeLong. See the attached timing diagram for a visual representation.

However, in testing the code there do not appear to be distinct timing phases as intended (oscillation/standby). Instead, the LEDs flash for timeLong when timeShortPercentage is 50%. When timeShortPercentage is set to 100%, the LEDs flash for twice timeLong. The LED’s flash at their respective frequencies until one “cycle” completes and then turn off forever, with no repeating the cycle.

Code

The basic premise of my code is to initialize a list (rowList) of Row class objects that have a variety of parameters and functions available to them. Then, each iteration of the loop() function attempts to compare the currentTime given the millis() function and an offset (my attempt at having the timing cycle repeat) to timeShort to determine which phase of the cycle the LEDs are in. If it’s oscillation time, the LEDs flash using functions that calculate the periods ON/OFF using a given dutyCyclePercentage and compare the currentTime to the last time the LED was flashed to figure out if its time to switch the state of the LED. Once all the rows have been assessed, the loop() function repeats through the rows, ideally indefinitely (millis() rollover in a few days aside).

Conclusion

I have attached my code and a timing diagram to make sense of my goal. My code has a lot of variables and timing elements that make the whole thing pretty confusing, so I would really appreciate any help to figure this out. I have a basic understanding of the millis() function, but its possible I’m missing something about the actual workings of the board when it comes to timing and iterating through a loop, many instances of which don’t all happen at the same time, but I make the assumption that they do in a few places in my code. Thanks!

PWM_IndividualRows.ino.ino (3.95 KB)

aaaaa.pdf (101 KB)

Disclaimer: not much of a C++ programmer

You have a number of arrays which seem to be default values

double intensity[ROW] =               { 40, 40, 40, 40, 40, 40, 40, 40}; // Intensity of each row from 0-255                                                              (Intensity units?)
double frequency[ROW] =               { 6, 5,  5, 5, 5, 5,  5, 5}; //  Fruency of short term oscillations (try to keep below 10 Hz)                                              (Hz)
double dutyCyclePercentage[ROW] =     {50, 50, 50, 50, 50, 50, 50, 50}; //  Duty cycle of short term oscillations                                                                 (%)
double timeLong[ROW] =                {10, 10, 10, 10, 10, 10, 10, 10}; //  long term cycle time                                                                                  (ms)
double timeShortPercentage[ROW] =     {100, 100, 100, 100, 100, 100, 100, 100}; //  percentage of long term cycle, during which short term oscillations occur                             (%)

You can get rid of them if you change the setUp method in the class to below

    void setUp(int pin, double intensity = 40, double frequency = 5, double dutyCyclePercentage = 50, double timeShortPercentage = 100, double timeLong = 10) {
  ...
  ...
}

And your standard setup() will change to

void setup() {

  // the first row has a different intensity by default
  rowList[0].setUp(row[0], 40, 6);
  pinMode(rowList[0].getPin(), OUTPUT);

  // initialize the other rows
  for (int i = 1; i < ROW; i++) {
    rowList[i].setUp(row[i]);
    pinMode(rowList[i].getPin(), OUTPUT);
  }
}

Note that for the first row, you have to pass 2 additional variables although only one is different to be able to change the frequency.

I know that you don’t run out of memory, but the code size goes down by roughly 650 bytes and the SRAM usage by 160 bytes (272 → 112).

Next comment is on this line in the global section.

int i = 0; //  cycle thru all rows

You should never use single character variables as global variables; it will be a mission to find them everywhere in your code; everytime you will e.g. find the ‘i’ in ‘if’, ‘while’ etc. A far better (and probably more sensible) name would be index or rowIndex

Next comment would be on the use of timing. Why do you use doubles for that? millis() returns an unsigned long and all timing related variables should be unsigned longs. It is a bit too much work for me to modify it everywhere without the risk of breaking anything (can’t test).

I think that the remainder of the code is OK; if it works, it works :wink: