9

I have an problem, which at first thoughts (and being new to Arduino) I though was a perfect application for an Arduino. However, after trying and failing to implement it I am doubting myself!

Simply - I need to control many LEDs independently, many of which will have their own individual patterns - for example "5 seconds on - 5 seconds off". "continuous flashes" - or sequences such as "2 flashes, pause, 1 flash". Obviously without the luxury of threads I am becoming a little unstuck. Be great to hear if a) Arduino is the best choice and b) if it is - how can I go about this!

Thanks in advance :)

2
  • 1
    Have you looked into protothreads? There are a couple of Arduino libraries that let you easily incorporate protothreads in your project.
    – sachleen
    Commented Jul 10, 2014 at 20:46
  • Use Arduino library NonBlockingSequece. It has many clean examples to answer this specific question. Commented Feb 18 at 0:20

3 Answers 3

9

Handling multiple patterns at the same time is certainly possible with a platform like Arduino, and there are a number of ways you could go about it.

One method I would consider is writing functions which effectively represent each pattern mathematically. You'd just pass it the total time that's elapsed in your program so far, and it will do the appropriate action for that specific moment in time. It will return immediately afterwards (no delays or anything).

To do that, you'll first need to know how long a single cycle of the pattern lasts. You can then use the modulo operator to figure out how far through the current cycle you are. From there, all you need to do is have some if conditions to determine what to do at any given time.

Here's what it might look like for your "5 seconds on, 5 seconds off" pattern:

function pattern5on5off(unsigned long totalTime)
{
  // Calculate how far through the current cycle we are
  const unsigned long cycleTime = totalTime % 10000;

  // If we're in the first 5 seconds of the cycle then turn the light on.
  // Otherwise, turn it off.
  if (cycleTime < 5000)
    digitalWrite(3, HIGH);
  else
    digitalWrite(3, LOW);
}

Admittedly, constantly calling digitalWrite() when you don't technically need to isn't very efficient. It shouldn't cause any harm though, and is fairly easy to optimise if necessary.

To use the above example in a sketch, you'd just need to call it in loop(), and pass the number you get from millis(); e.g.:

void loop()
{
  const unsigned long totalTime = millis();

  pattern5on5off(totalTime);

  // call other patterns here...
}

Other patterns will be more complex, but follow the same principle. You'd just to use appropriate if statements to express your logic.

The vital thing to remember is that the function represents a specific moment in time. It should never pause or delay the program, otherwise it will prevent the other patterns from running.

Edit: Timing on the first cycle
As jfpoilpret noted in the comments, the very first cycle will start at a random point. This is because the first time you call millis() in loop(), it won't start at 0 (the device will already have been running for a short time before loop() gets called). It's easy to resolve though, if necessary.

You would do it by offsetting the totalTime value by the whatever value you got on the very first time around loop(). For example:

unsigned long g_startTime = 0;

void loop()
{
  unsigned long totalTime = 0;

  if (g_startTime == 0) {
    // This is the first cycle.
    // Store the start time so we can compensate later.
    g_startTime = millis();

  } else {
    // This is not the first cycle.
    // Compensate for the start time.
    totalTime = millis() - g_startTime;
  }

  pattern5on5off(totalTime);
  // etc..
}
4
  • Thank you so much - makes perfect sense! Ive definitely been banging my head against a wall with the wrong approach... :)
    – Nickos
    Commented Jul 10, 2014 at 20:27
  • 1
    The problem with the % approach is that the timing won't be correct the very first time, as it will be just random at first.
    – jfpoilpret
    Commented Jul 11, 2014 at 4:57
  • 1
    @jfpoilpret That's true. It's easy to fix though, so I've added it to my answer. :) Commented Jul 11, 2014 at 8:50
  • Another option would be, instead of running millis once in the loop and pass its value as a parameter to every led function, to have each function as a parameterless one, and run millis inside each one. That would allow each function to get a more precise value, which might or might not be important depending on how much time each function in the loop takes to run, and also the timing correctness requirements of the application. Commented Jul 5, 2015 at 23:30
4

Arduino is a fine choice for the task - easy to get started with. The key is to write non-blocking code. You could take a look at the BlinkWithoutDelay example.

I made a suggestion for your task:

// Timing suquences for the LED's in milliseconds
// First value is on time, second value is off time,
// third value on time and so on (up to 10 values)
// One row for each LED
unsigned int led_timing[][10] = {
  {5000, 5000},
  {100, 1000},
  {100, 100, 100, 1500, 100, 1500}
};

// The pins the LED's are connected to
byte led_pins[] = {11, 12, 13};

// Keep track of timing sequence
// Array size taken from led_pins
unsigned long last_change[sizeof(led_pins)/sizeof(led_pins[0])];
byte timing_i[sizeof(led_pins)/sizeof(led_pins[0])];

void setup()
{
  // Initialize LED's as output
  for (byte i = 0; i < sizeof(led_pins)/sizeof(led_pins[0]); i++)
  {
    pinMode(led_pins[i], OUTPUT);
    digitalWrite(led_pins[i], HIGH);
  }
}


void loop()
{
  // Current timestamp
  unsigned long now = millis();

  // Keep track of sequence for each LED
  for (byte i = 0; i < sizeof(led_pins)/sizeof(led_pins[0]); i++)
  {
    if (now - last_change[i] >= led_timing[i][timing_i[i]])
    {
      digitalWrite(led_pins[i], !digitalRead(led_pins[i]));
      timing_i[i]++;

      // Start over at the end of timing sequence
      timing_i[i] %= sizeof(led_timing[i])/sizeof(led_timing[i][0]);

      last_change[i] = now;
    }
  }
}
1

I know the post is old, but I checked the example with the array based approach and in my opinion:

sizeof(led_timing[i])/sizeof(led_timing[i][0])

will always yield the allocated size (number of elements) of the array - in this case 10. So the timing won't restart until you reached the "end" of the array, using undefined elements. For LED "0":

int led_timing[0][10]:
5000,5000, <undefined>, <undefined>, <undefined>, <undefined>, <undefined>, <undefined>, <undefined>, <undefined>

Greetings Tommy

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.