Multiple LEDs, same flash rate but different patterns

Hello,

I am new to Arduino and wanted to teach myself some principles by building a little project to simulate the lights on marine Cardinal Marker buoys, here is a link to what it should look like:

As you can see it is just 4 flashing LED’s. Three of the lights follow a 15 second pattern while one, the EAST marker repeats every 10 seconds.

I have written a very basic code using DELAY to write to each LED. It is very inelegant and means that ALL the lights follow a 15 second pattern which is wrong.

I quickly realised I should not use delay and perhaps an array and millis function is the way to go.

I have tried to take chunks of other peoples examples to make this work but do not understand enough to get the LEDs to flash a certain number of times before repeating. I think perhaps I need the counter function.

I have used this to write a short code for each light in isolation and this works but I cant work out how to get them all to start together and flash in sync.

I would be grateful if someone could show me the correct code that I could pick apart to understand the logic. It is frustrating because I think this should be very basic and short!

Regards,

Harry

See Step 2: How to Write a Non-blocking Delay in Arduino

#define DELAY_1 7500
#define DELAY_2 5000
// ... etc

uint32_t timer1;
uint32_t timer2;
// ... etc

void setup() {
  timer1 = millis();
  timer2 = timer1;
  // ... etc
}

void loop() {
  uint32_t now = millis();
  if (now - timer1 >= DELAY_1)
  {
    //toggle led 1
    timer1 += DELAY_1;
  }
  if (now - timer2 >= DELAY_2)
  {
    //toggle led 2
    timer2 += DELAY_2;
  }
  // ... etc
}

I looked at the animated gif. Although you can use millis() instead of delay(), unless there are other functions that need to be performed in real time, there is no real need for non-blocking code in this case. That is because there are no on-off transitions that overlap. So if you have your patterns stored in an array, you already have the most "elegant" part made. You made reference to synchronization, yes you have to manage that, and it is easier to do that with delay() than millis().

If you want something basic and short, delay() will get you there faster than millis(). The pattern array can be as small as 15 bytes, where every byte contains 4 bits to signify flash/no-flash for each cycle.

If you want something basic and short, delay() will get you there faster than millis(). The pattern array can be as small as 15 bytes, where every byte contains 4 bits to signify flash/no-flash for each cycle.

This is true, but only if all the sequences have the same duration.

As you can see it is just 4 flashing LED's. Three of the lights follow a 15 second pattern while one, the EAST marker repeats every 10 seconds.

The OP seems to be indicating that they don't, and so I suspect this is where they are getting stuck.

If you want to do this with delay() then you need to make all the flash patterns the same overall duration. This means finding the lowest common multiple.

For 15 and 10 second repeats that's 30 seconds. So:
Repeat the 15 second pattern 2 times (2 x 15 = 30 seconds), while at the same time repeat the 10 second pattern 3 times (3 x 10 = 30 seconds).
Now both flash patterns are "in step" and can continue again from the beginning.

For a short and simple repeating pattern, there is no need to fabricate logic to implement the steps. It's so easy to just do a table look up. You don't need to consider them as separate patterns, just because they can be viewed that way. Due to the need for synchronization, there is essentially only one repeating pattern. If it's 30 vs. 15 bytes, it's still a very small table.

pcbbc:
This is true, but only if all the sequences have the same duration.
The OP seems to be indicating that they don't, and so I suspect this is where they are getting stuck.

If you want to do this with delay() then you need to make all the flash patterns the same overall duration. This means finding the lowest common multiple.

For 15 and 10 second repeats that's 30 seconds. So:
Repeat the 15 second pattern 2 times (2 x 15 = 30 seconds), while at the same time repeat the 10 second pattern 3 times (3 x 10 = 30 seconds).
Now both flash patterns are "in step" and can continue again from the beginning.

Thank you, yes, this was my concern. I need to cater for two different patterns. I was leaning toward increasing my loop from 15 to 30 steps. It would work but seemed like a long winded code, however it may be that there is no other shorter way of doing it.

Long winded? I envision about 20 lines of code. A 30 byte table that you just cycle through continually, writing the values to the lights with delay timing... easy peasy.

harry_spillett:
It would work but seemed like a long winded code, however it may be that there is no other shorter way of doing it.

For this example it’s probably the shortest and easiest to understand and implement. And it’s not long winded if you drive the sequence from a lookup table.

However you do indicate this is a learning exercise, and so there is probably some merit in also attempting the problem the more complex way.

That will stand you in good stead when you want to do several asynchronous tasks in the future.

Any number of LEDs each with different on/off periods if required and a series of different periods if required

const byte ledPins[] = {3,5,6,9};
const byte NUMBER_OF_LEDS = sizeof(ledPins);
unsigned long startTimes[NUMBER_OF_LEDS] = {};
byte indexes[NUMBER_OF_LEDS] = {0};
const byte MAX_NUMBER_OF_PERIODS = 10;
unsigned long periods[][MAX_NUMBER_OF_PERIODS] = //on/off periods for each LED.  zero indicates end of sequence
{
  {1000, 2000, 1500, 2500, 0},
  {500, 200, 1000, 2000, 3000, 4000, 0},
  {400, 1000, 1500, 2000, 0},
  {1100, 2200, 0}
};

void setup()
{
  Serial.begin(115200);
  for (int led = 0; led < NUMBER_OF_LEDS; led++)
  {
    pinMode(ledPins[led], OUTPUT);
  }
}

void loop()
{
  unsigned long currentTime = millis();
  for (int led = 0; led < NUMBER_OF_LEDS; led++)  //iterate through the LEDs
  {
    if (currentTime - startTimes[led] >= periods[led][indexes[led]])  //? time to change ?
    {
      digitalWrite(ledPins[led], !digitalRead(ledPins[led]));  //flip led state
      startTimes[led] = currentTime;  //save start time of state
      indexes[led]++;                    //increment index
    }
    if (periods[led][indexes[led]] == 0)  //if next period is zero (end of sequence)
    {
      indexes[led] = 0;        //reset period index for this LED
    }
  }  //end for loop
}

aarg:
Long winded? I envision about 20 lines of code. A 30 byte table that you just cycle through continually, writing the values to the lights with delay timing... easy peasy.

I have not tried a lookup table before. I originally wrote out each step like this:

//Step 1
digitalWrite(NORTH, HIGH);
digitalWrite(EAST, HIGH);
digitalWrite(SOUTH, HIGH);
digitalWrite(WEST, HIGH);
delay(FLASH);
digitalWrite(NORTH, LOW);
digitalWrite(EAST, LOW);
digitalWrite(SOUTH, LOW);
digitalWrite(WEST, LOW);
delay(DARK);

The on time is shorter than the off time hence the two delays.

I would need 30 steps like this to alter each LED for each step. This seemed like a lot of lines to achieve my goal. I also considered using port manipulation (ie PORTD = B10101000;) to write each step but have read that this should be avoided as it can make debugging difficult.

I will investigate a look up table if you think it will take up fewer lines than my current method.

Thank you.

Instead of...

 startTimes[led] = currentTime;

To avoid drift and inaccuracies you should code...

 startTimes[led] += periods[led][indexes[led]];

@harry: Yes, that would be long winded.
UKHeliBob posted a great starting point t for you. Although note my comments above as relative drift will be important in your application (you need all the LEDs to remain in sync).

pcbbc:
Instead of...

 startTimes[led] = currentTime;

To avoid drift and inaccuracies you should code...

 startTimes[led] += periods[led][indexes[led]];

@harry: Yes, that would be long winded.
UKHeliBob posted a great starting point t for you. Although note my comments above as relative drift will be important in your application (you need all the LEDs to remain in sync).

Thank you both. I will try to modify UKHeliBobs code to suit my project and use your suggestion to prevent drift between the LEDs.

aarg:
I looked at the animated gif. Although you can use millis() instead of delay(), unless there are other functions that need to be performed in real time, there is no real need for non-blocking code in this case. That is because there are no on-off transitions that overlap. So if you have your patterns stored in an array, you already have the most “elegant” part made. You made reference to synchronization, yes you have to manage that, and it is easier to do that with delay() than millis().

If you want something basic and short, delay() will get you there faster than millis(). The pattern array can be as small as 15 bytes, where every byte contains 4 bits to signify flash/no-flash for each cycle.

I wrote a very long winded but simple code to get the flash patterns I need. As you suggested, I created a 30 step loop. This was very long so I am now trying to get the same result with a shorter code. I have tried the following:

const int FLASH = 250; // ON for 250mS.
const int DARK = 750; // OFF for 750mS thoerfore total flash time 1 second.

int FlashStep[30] =
{
B11110000,//1
B11110000,//2
B11110000,//3
B10110000,//4
B10110000,//5
B10110000,//6
B10110000,//7
B10110000,//8
B10010000,//9
B10000000,//10
B11000000,//11
B11000000,//12
B11000000,//13
B10000000,//14
B10000000,//15
B10110000,//16
B10110000,//17
B10110000,//18
B10110000,//19
B10110000,//20
B11110000,//21
B11110000,//22
B11110000,//23
B10010000,//24
B10000000,//25
B10000000,//26
B10000000,//27
B10000000,//28
B10000000,//29
B10000000,//30
};

int FlashCount = 30;

void setup()
{
DDRD = DDRD | B11110000; // set pins 4 to 7 as outputs without changing the value of pins 0 & 1, which are RX & TX
}

void loop()
{

for (int thisPin = 0; thisPin < FlashCount; thisPin++) {
// turn the pins on:
PORTD = FlashStep[thisPin];
delay(FLASH);
// turn the pins off:
PORTD = B00000000;
delay(DARK);
}

}

This seems to work well and is much shorter, however for steps 7 and 8 as well as 22 an 23 one of the lights should stay lit ie for 2 seconds without turning off. How would I alter the code to reflect this?

Does this seem to be the most efficient way to code this sort of thing?

Thank you for your help so far,

Harry

One way:

Double the size of the table. Let each *pair * of entries, 0-1, 2-3, etc. control the on/off state of the LEDs - the mass clearing of the outputs goes away. Now you control both states individually for all LEDs. If a certain one is to stay on, or off, just set its table position bit accordingly.

Replace the for loop with a while loop. For the 250ms part output the even table location and increment the array index. When the 250ms is up output the 750ms (odd) pattern and again increment the array index.

void loop()
{
  int thisPin = 0;
  while ( thisPin  < FlashCount) {
    // 250ms states
    PORTD = FlashStep[thisPin++];
    delay(FLASH);
    // 750ms states
    PORTD = FlashStep[thisPin++];
    delay(DARK);
  }