Andrew Dodd

 Home

LED Flash Patterns...for the last time - Part 1

Every embedded product since the first Blinky program has had its own variety of LED flash patterns. You know the ones I mean, flash green once every two seconds if everything is ok, and once every half a second if something bad has happened.

But it never stays that simple. There’s always someone who says “that’s great…but for serious errors can you flash like this, and for warnings like that”, and before long you have a plethora of patterns that make the POST beep patterns look paltry. And then some hardware engineer offers to add Bi-colour and Tri-colour LEDs to the board and the twinkle in the product manager’s eye becomes a kaleidoscopic sparkle…

Motivations

The motivations I have in presenting this technique for implementing LED flash patterns are:

  • Making the pattern somewhat obvious, even to non-technical people
  • Providing an implementation that keeps patterns in sync
  • Providing an implementation that can choose if it wants to be permissive or not
  • Outlining a way to implement LED Flash Pattern Controlling, while not caring too much about the actual LED control (i.e. depending on the details)
  • Exploring some of the options you have for extending the idea

But first I will cover the usual designs you see in this field.

The Electrical Engineer Way

Much of the world’s embedded products (and thus software) are brought to life by or under the auspices of The Electrical Engineer. Electrical engineers are taught many complex and diabolical topics; are often very capable thinkers and problem solvers; and are generally a little myopic (I too trained as one, so feel pretty safe stating this…also I’m a bit myopic).

The initial functional descriptions for the “LED” features usually include high level items like “control the brightness”; “flash at different rates” and have “long and short flashes”. The Electrical Engineer typically reaches for a common abstraction they are intimitely familiar with…the square wave. A square wave fits the bill perfectly:

  • The FREQUENCY of the wave will control the flash rate
  • The width of the pulse (the DUTY CYCLE) will control the length of the flash
  • The PHASE of the wave could be used to do some cool stuff in the future (never mind that a Knight Rider sequence is about as useful in a real product as a bootloader that cannot rollback after a failed firmware upgrade attempt…)

Obviously this list is missing the brightness feature, but luckily for The Electrical Engineer they can use the square wave idea recursively! Brightness can be controlled by modulating the ON signal of a light with another (much higer frequency) square wave. This is the famous Pulse-Width-Modulation technique, usually offloaded to a hardware block well suited to this purpose!

This model breaks down pretty fast for the flash pattern control case (it remains robust for the brightness case), as microcontrollers have a limited number of hardware timers which makes any frequency hard to support. It also breaks down pretty quickly for multi-colour LEDs and for “double tap” and “SOS” style patterns. So the next stop is usually something like…

The Embedded Software Way

What usually follows is some form of array of states. I have been guilty of doing this many times, both in the uniform-duration method (where every array item represents say 100ms of time), or in the fully flexible style I’ll illustrate below:

enum {
  MAX_ELEMENTS = 10,
};

enum {
  OFF    = 0,
  BLACK  = 0,
  GREEN  = 1,
  YELLOW = 2,
  RED    = 3,
};

struct PatternElement {
  uint16_t duration;
  uint8_t  color;
};

struct Pattern {
  uint8_t length;
  struct PatternElement elements[MAX_ELEMENTS];
};

// A 0.5 Hz Green flash at 50% duty
struct Pattern HEALTH_GOOD = {
  .length = 2,
  .elements = {{.duration = 1000, .color=GREEN },
               {.duration = 1000, .color=OFF },
  }
};

// A 1.25 Hz Yellow flash at 25% duty
struct Pattern HEALTH_OK = {
  .length = 2,
  .elements = {{.duration = 200, .color=YELLOW },
               {.duration = 600, .color=OFF },
  }
};

// A 2.0 Hz Red Yellow Green Multi-tap flash
struct Pattern HEALTH_BAD = {
  .length = 6,
  .elements = { {.duration = 125, .color=RED },
                {.duration = 250, .color=OFF },
                {.duration = 125, .color=YELLOW },
                {.duration = 250, .color=OFF },
                {.duration = 125, .color=GREEN },
                {.duration = 1125, .color=OFF },
  }
};

You can see this gets verbose very quickly, and also often suffers from violating the DRY guideline by having a “length” field that is related to the number of elements in the pattern but is not enforced to be correct (sure…you could have a NULL or a terminating element with duration=0 but they are not exactly safe options either). In order to cope with requirements that don’t fit the Square Wave model (like double-tap patterns) the question of brightness has been dropped, and phase has been hidden in the relative sums of the durations of each PatternElement (i.e. is it OFF first then RED, or RED first then OFF?).

Needless to say, this model is extremely painful to work with, especially for non/less-technical people. I accidentally discovered the next model when writing a comment trying to document the exact flash pattern one of these arrays of elements designs was representing…

The Mystique of the Hidden Way

The solution is so obvious it must be a hidden way, so mysterious that I stumbled on it…

You just use a string.

And then you choose a base rate that trades off the ability to vary patterns, vs. the memory to store the pattern and the convenience to read it.

For example the patterns above might be implemented like this:

// Patterns assume a base rate of 125ms, where each character is should for 125ms
char const * const HEALTH_GOOD = "GGGGGGG        ";
char const * const HEALTH_OK   = "Y    ";            // Slightly different -> 1.25Hz @ 20%
char const * const HEALTH_BAD  = "R__Y__G_________";

Right, that’s simple…but how do you use these patterns in any meaningful way?

But how to?

This post is already getting a bit long, so instead of diving into example code for the whole thing I thought it would be best to do a simple example showing how you might implement this in its basic form, without brightness, without colour, and without any attempt to structure for “reuse”.

The following is an Arduino sketch that implements this idea by extending the standard “Blink” example to flash three different patterns, one each for 6 seconds:

/*
  Basic LED patterns - Taken from Blink example

  Flashes a LED in a variety of patterns, switching between 
  a few "rhythms" every 6 seconds.
*/

const long LoopDelay = 125;  // The delay in ms for each loop
long loopCount = 0;

//   Name of the beat       Simplest           Easiest to compare
//                           form
char four_to_the_floor[] =  "X-----";       // "X-----X-----X-----X-----";
char swung_beat[]        =  "XXX---X---X-"; // "XXX---X---X-XXX---X---X-";
char three_four[]        =  "XX------";     // "XX------XX------XX------";

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  updateLED(loopCount);
  loopCount++;
  delay(LoopDelay);
}

void updateLED(long loops) {
  char *pattern;
  
  long milliSecs = loops * LoopDelay;
  
  // truncate to 18 seconds
  milliSecs %= 18000;
  
  // Based on the milliseconds, choose a beat
  if (milliSecs < 6000) {
    pattern = four_to_the_floor;
  } else if (milliSecs < 12000) {
    pattern = swung_beat;
  } else {
    pattern = three_four;
  }
  
  // Determine 'where' in the pattern, making sure to take the 
  // length of the chosen pattern
  int idx = (milliSecs / LoopDelay) % strlen(pattern);
  bool isOn = pattern[idx] == 'X';
  digitalWrite(LED_BUILTIN, isOn);
}

You should be able to load this into any Arduino to see how it works (or alternatively just read it!).

Ground-breaking? No. Neat? Yes.

It’s certainly not a ground breaking idea, but it makes it simple for anyone to see what the pattern will be. It is a mini-DSL for flash patterns that everyone can understand, it’s almost this simple.

I’m not sure if there is much more to summarise. It’s pretty sad that the net outcome of this post is “just use a string”…but I’ve seen enough complicated flash pattern definitions and handling to feel that it is worthwhile. It should definitely be more useful after the next post on how to get more leverage from the idea (i.e. how to have those multi-colour patterns every PM wants).

In the next post I hope to cover:

  • How to implement this behaviour as a more reusable service
  • How to be permissive or strict on the patterns (e.g. should "X___" and "X---" both work?)
  • How to start thinking about abstracting the colour encoding from the actual LED drive pins