algorithm Arduino software

MultiBlink – Simplifying concurrent LED patterns

A long time ago (2012) I wrote an application called MultiBlink that allowed me to control many simultaneous independent monochrome LEDs lighting in patterns. The original version was updated a number of times, gaining more features (and complexity) with each iteration. Recently I updated MultiBlink to version 5, which included a conversion to using neopixel type RGB LEDs controlled using the FastLED library.

What is MultiBlink?

MultiBlink (available from my code repository) is a sketch that implements a data driven approach to simultaneously animating lots of LEDs at different frequencies, include fades, delays and loops in patterns.

The sketch is useful for implementing patterns used in scale models (eg, airplanes, boats, trains sets) or LED chaser sequences. One application has used MultiBlink to animate a graphic with lighthouses in the actual colors they flash. Basically it is suitable for any application that needs to maintain steady patterns of blinking lights.

The code used in MultiBlink implements a general approach to controlling the LED animations using a Finite State Machine. What to do in each state of the FSM is defined using commands arranged in data tables that are ‘run’ by the overall FSM. To change the behavior of the software, the data table is modified for the new requirements/patterns.

This separation between code and data, whilst slightly complicating the code, has a substantial advantage when it comes to defining new LED patterns. How we blink LEDs is the same no matter which LED we are blinking, so this can be defined independently. Which LEDs are doing the blinking is separately defined and applied to the how code.

The sequence tables consist of an arbitrary length sequence of data structures that define the parameters for the neopixels and ‘remember’ the current FSM parameters – for example, its current state and how long it should last.

Each sequence entry has associated with it a number of states that specify what the FSM will do when it is in that state. The overall effect is that the sequence tables work like a macro language to direct the how the LEDs should be animated.

Changing MultiBlink to use Neopixel RGB

The evolution of this application, traced in the different versions at the code repository, is an interesting view of how simple code can become more complex at the same time as becoming a more useful.

The latest version of MultiBlink (Multi_Blink5_Neo) upgraded the LED management from direct I/O and simple monochrome on/off and PWM control to using neopixel style serially controlled LEDs.

The first and most obvious change is that what were simple ON/OFF states now need to define the target color. For convenience the sketch used the FastLED CRGB color definition to store this. This required more storage space and so the tables also needed to be moved to PROGMEM to allow for other functionality in the smaller processor types.

A second, not so obvious, change is that LEDs also no longer fade between two predefined states (ON and OFF) using PWM on a digitally controlled output. In fact, LED fades occur between an arbitrary start and and end color, requiring a more complex algorithm to manage the change. I discussed a suitable algorithm in this previous post, based on Bresenham’s algorithm, drawing a 3D line between 2 points in RGB color space.

A third change that was needed in the sketch was to fix the small historical discrepancies in time keeping that were magnified by the slower serial communications to the LEDs.

Keeping strict sequence transition timing has the highest priority in the MultiBlink application. Multi sequence LED patterns must remain in synch ‘forever’ and even a few fractions milliseconds difference in timing can make LEDs become very obviously out of synch in the long run. This change affected how the time was handled in many paces in the sketch to make it much stricter than it had previously needed to be.

Defining MultiBlink Sequences

MultiBlink sequences are a collection of transitions and actions that are executed by the FSM. These include transitions for setting and fading a led, looping and unconditional jumps inside the sequence.


Keeps the LED on the specified color for the specified time. The macro SET(t, c) is used to statically initialize the set parameters, where t is the time to keep the color c on.

For example, the entry

{ SET(25, CRGB(255,255,255) } 

keeps the output at the RGB color (255,255,255) for 25 milliseconds.


Make the pattern loop back to the nominated array element in the sequence for the specified number of times after a specified delay. The macro DO(t, j, n) is used to set the loop parameters, where t is the
delay time before testing the loop, j is the jump-to array index (zero
based) to loop back to and n is the total number of times to execute the
loop. A sequence will automatically loop back to the start when it reaches the end (an implied MB_GOTO to the start of the sequence).

For example, the entry

{ DO(10, 0, 3) }

causes the FSM to delay 10 milliseconds and then loop back to element 0.
The process is repeated it 2 more time (3 total).


Fades a led from a starting color to an end color (both FastLED CRGB values) over a specified time period. This is, in effect, a special kind of loop that interpolates between two colors. The macro FADE(t, c1, c2) is used to initialize the fade effect parameters in the data structure, where t is the overall time to fade, c1 and c2 are the start and end color CRGB values, respectively.

For example, the entry

{ FADE(500, CRGB(0,0,0), CRGB(255, 255, 255) }

fades a led between CRGB colors (0,0,0) and (255,255,255) over 500 milliseconds.


Go to the state specified as the next state, with an optional time delay
before jumping to the new state. The macro GOTO(t, j) is used to initialize the goto parameters in the data structure, where t is the time before jumping to the zero-based jump-to sequence index j. The value of t is set to 0 for an immediate jump.

For example, the entry

{ GOTO(55, 2) }

goes to state 2 after 55 milliseconds have elapsed.


Stop executing the pattern. It disables a pattern from running until is re-enabled by sketch code outside the FSM. The macro STOP is used to create this sequence entry.


This is a place marker for sequence entries that are unused. These entries are skipped. The macro NOP (no operation) is used to create this sequence entry.

Building Sequence Table Entries

Whilst sequence entries are defined and run independently, the patterns can also be constructed by considering and planning how these independent LEDs interact from an viewer’s perspective.

For example, this data for LED index 3

{ 3, {SET(25, CRGB(0,0,0), SET(25, CRGB(255,255,255), DO(0, 0, 3), SET(300, CRGB(0,0,0)} },

executes the following sequence:
State 0 is LED at RGB BLACK for 25 ms
State 1 is LED at RGB WHITE for 25 ms
State 2 is loop back to state 0 and repeat it 2 more time (3 total)
State 3 is LED at RGB BLACK for 300 ms – this is a delay while the ‘other’ LED blinks (see below)

So if we want to have a paired LED that works in tandem with this one and is blinking while this one is off, we need to implement the complementary seqeuence

State 0 is LED at RGB BLACK for 300 ms – this is a delay while the ‘other’ LED blinks (see above)
State 1 is LED at RGB WHITE for 25 ms
State 2 is LED at RGB BLACK for 25 ms
State 3 is loop back to state 1 and repeat it 2 more time (3 total)

which defines the following sequence entry for output led 4

{ 4, {SET(300, CRGB(0,0,0), SET(25, CRGB(255,255,255), SET(25, CRGB(0,0,0), DO(0, 0, 3)} },

So, finally, if we look at them as a pair

{ 3, {SET(25, CRGB(0,0,0), SET(25, CRGB(255,255,255), DO(0, 0, 3), SET(300, CRGB(0,0,0)} },
{ 4, {SET(300, CRGB(0,0,0), SET(25, CRGB(255,255,255), SET(25, CRGB(0,0,0), DO(0, 0, 3)} },

Similarly, more complex patterns and interactions can be planned and configured into the data tables.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s