Take the 2-minute tour ×
Arduino Stack Exchange is a question and answer site for developers of open-source hardware and software that is compatible with Arduino. It's 100% free, no registration required.

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 :)

share|improve this question
1  
Have you looked into protothreads? There are a couple of Arduino libraries that let you easily incorporate protothreads in your project. –  sachleen Jul 10 at 20:46

2 Answers 2

up vote 5 down vote accepted

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..
}
share|improve this answer
    
Thank you so much - makes perfect sense! Ive definitely been banging my head against a wall with the wrong approach... :) –  Nickos Jul 10 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 Jul 11 at 4:57
1  
@jfpoilpret That's true. It's easy to fix though, so I've added it to my answer. :) –  Peter R. Bloomfield Jul 11 at 8:50

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;
    }
  }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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