4: Getting the sample code

The code uses the FastLED library, which can be added to the IDE through the Library Manager. Depending on the version of the IDE you use, it may look a little different, but on a Mac, it looks like this:

A search for FastLED should turn up the library we need, and clicking “Install” makes it ready to use. After this, we can create a new sketch (the Arduino IDE name for a program) with the original code and change it to what we want! The original code is at the bottom of this page.

Original code:

#include <FastLED.h>

// LCDS LED Widget starter code, June 2019
// 
// bdodds at gmail dot com
//
// Read through the comments below and play around and have fun!


// *********************************************************
// First we set up things for the FastLED library to work
// These shouldn't be messed with: 

FASTLED_USING_NAMESPACE

#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000)
#warning "Requires FastLED 3.1 or later; check github for latest code."
#endif

#define DATA_PIN    7
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS    20
CRGB leds[NUM_LEDS];
// *********************************************************


// *********************************************************
// Now we can start playing
//
// Change the brightness of the LEDs here: 
// 
#define BRIGHTNESS         75
//
// The number is from 0 to 100 and it is the percentage duty cycle,
// or how long the lights are on in any given time so if the number 
// is 50, the LEDs will flash on as much as they are off.  LEDs are 
// different than normal bulbs in that they operate best at the full
// power they can take, so instead of using a dimming method that 
// lowers the voltage, it flashes the LEDs very quickly, quicker 
// than humans can see.  The more time the LEDs are on, the brighter
// they appear.  A brightness of 50 means they are on as much as they
// are off, and so on.  


// This controls the overall speed of the animations, and is set at 
// what I determined to be optimal, but feel free to play with it.  
// It's originally set to 120:
#define FRAMES_PER_SECOND  120


// Now we have functions.  Functions are groups of commands that are
// grouped together to do a more complex task. The first function is the 
// setup() function that runs once when the board is first powered on.
// It's unlikely you'll want to change anything in here:

void setup() {
  delay(300); // 300 millisecond delay for recovery  
  // tell FastLED about the LED strip configuration
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  // set master brightness control
  FastLED.setBrightness(BRIGHTNESS);
}

// Now we set some variables for our functions to use later.
// First up is a list of patterns to cycle through.  Each of the patterns
// is defined as a separate function below. Change the ordr of these
// patterns to change the order they run through on the board.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { LCDS, rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm, cylon, fire };

// Index number of which pattern is current
uint8_t gCurrentPatternNumber = 0; 
// rotating "base color" used by many of the patterns
uint8_t gHue = 0; 
  

// This next function is the main function that runs on the board.  This will loop
// indefinitely until the board is powered off.  
void loop()
{

  // This calls the function that runs through all of the patterns defined
  // above.  Comment out this line (put a // in front) if you don't want
  // the patterns to cycle through:
  gPatterns[gCurrentPatternNumber]();

  // These are the individual patterns called by each function.  Uncomment 
  // them to display just that one pattern, assuming the above line is 
  // commented out.
  
  //LCDS();
  //rainbow();
  //rainbowWithGlitter();
  //confetti();
  //juggle();
  //sinelon();
  //bpm();
  //cylon();
  //fire();

  // send the 'leds' array out to the actual LED strip
  FastLED.show();  
  // insert a delay to keep the framerate modest
  FastLED.delay(1000/FRAMES_PER_SECOND); 

  // slowly cycle the "base color" through the rainbow
  EVERY_N_MILLISECONDS( 20 ) { gHue++; } 
  // change patterns periodically, change the number to make the patterns cycle
  // at a different rate
  EVERY_N_SECONDS( 10 ) { nextPattern(); } 
}

#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

void nextPattern()
{
  // add one to the current pattern number, and wrap around at the end
  gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);
}

// This function shows the new LCDS logo colors on the letters:
void LCDS()
{
  static uint8_t hue = 0;
  int colors[] = {160,120,90,30};
  int index=0;
  int fade = 380;
  // Spell out LCDS, start with the L:
  for( int i = 0; i < 5; i++) { 
    leds[i] = CHSV(colors[index % 4], 255, 255);
  }
  index++;
  FastLED.show();
  delay(100);
  fadeToBlackBy(leds, NUM_LEDS, fade);
  // Spell the C:
  for( int i = 5; i < 10; i++) { 
    leds[i] = CHSV(colors[index % 4], 255, 255);
  }
  index++;
  FastLED.show();
  delay(100);
  fadeToBlackBy(leds, NUM_LEDS, fade);
  // Spell the D:
  for( int i = 10; i < 15; i++) { 
    leds[i] = CHSV(colors[index % 4], 255, 255);
  }
  index++;
  FastLED.show();
  delay(100);
  fadeToBlackBy(leds, NUM_LEDS, fade);
  // Spell the S:
  for( int i = 15; i < 20; i++) { 
    leds[i] = CHSV(colors[index % 4], 255, 255);
  }
  index++;
  FastLED.show();
  delay(100);
  fadeToBlackBy(leds, NUM_LEDS, fade);
  hue=hue+10;
}

// FastLED's built-in rainbow generator
void rainbow() 
{
  fill_rainbow( leds, NUM_LEDS, gHue, 7);
}

// built-in FastLED rainbow, plus some random sparkly glitter
void rainbowWithGlitter() 
{
  rainbow();
  addGlitter(80);
}

void addGlitter( fract8 chanceOfGlitter) 
{
  if( random8() < chanceOfGlitter) {
    leds[ random16(NUM_LEDS) ] += CRGB::White;
  }
}

// random colored speckles that blink in and fade smoothly
void confetti() 
{
  fadeToBlackBy( leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV( gHue + random8(64), 200, 255);
  addGlitter(80);
}

// a colored dot sweeping back and forth, with fading trails
void sinelon()
{
  fadeToBlackBy( leds, NUM_LEDS, 20);
  int pos = beatsin16( 13, 0, NUM_LEDS-1 );
  leds[pos] += CHSV( gHue, 255, 192);
}

// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
void bpm()
{
  uint8_t BeatsPerMinute = 62;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
  for( int i = 0; i < NUM_LEDS; i++) { //9948
    leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
  }
}

// eight colored dots, weaving in and out of sync with each other
void juggle() {
  fadeToBlackBy( leds, NUM_LEDS, 20);
  byte dothue = 0;
  for( int i = 0; i < 8; i++) {
    leds[beatsin16( i+7, 0, NUM_LEDS-1 )] |= CHSV(dothue, 200, 255);
    dothue += 32;
  }
}

void fadeall() { for(int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(200); } }

void cylon() { 
  static uint8_t hue = 0;
  // First slide the led in one direction
  for(int i = 0; i < NUM_LEDS; i++) {
    // Set the i'th led to red 
    leds[i] = CHSV(hue++, 255, 255);
    // Show the leds
    FastLED.show(); 
    // now that we've shown the leds, reset the i'th led to black
    // leds[i] = CRGB::Black;
    fadeall();
    // Wait a little bit before we loop around and do it again
    delay(50);
  }

  // Now go in the other direction.  
  for(int i = (NUM_LEDS)-1; i >= 0; i--) {
    // Set the i'th led to red 
    leds[i] = CHSV(hue++, 255, 255);
    // Show the leds
    FastLED.show();
    // now that we've shown the leds, reset the i'th led to black
    // leds[i] = CRGB::Black;
    fadeall();
    // Wait a little bit before we loop around and do it again
    delay(50);
  }
}

// The rest of the code is for the fire() pattern, which makes the board
// look like flickering flame, from left to right. 

#define COOLING  55

// SPARKING: What chance (out of 255) is there that a new spark will be lit?
// Higher chance = more roaring fire.  Lower chance = more flickery fire.
// Default 120, suggested range 50-200.
#define SPARKING 120
bool gReverseDirection = false;

void fire()
{
// Array of temperature readings at each simulation cell
  static byte heat[NUM_LEDS];

  // Step 1.  Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
    }
  
    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for( int k= NUM_LEDS - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
    }
    
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(7);
      heat[y] = qadd8( heat[y], random8(160,255) );
    }

    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      CRGB color = HeatColor( heat[j]);
      int pixelnumber;
      if( gReverseDirection ) {
        pixelnumber = (NUM_LEDS-1) - j;
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}

3: Configuring the IDE

The Arduino board we used is compatible with the Arduino Nano with the old bootloader, so the board configuration should look like this:

The port will vary from system to system, but you should see the port appear after it’s connected to the computer. There may need to be some driver work to be done if it’s not appearing right away, so send a note through the contact form if that happens and we’ll get it sorted out!

2: Getting the Arduino IDE

The Arduino IDE is the software package we use to modify the code on the board that controls the LEDs. It is available for all major operating systems, so it shouldn’t matter if you use a Mac or Windows PC to program the board.

To download the software, go to https://www.arduino.cc/en/Main/Software and choose your operating system from the list, and follow the instructions. Once it is started, we can connect to the board and start programming!

Alternatively, we don’t even need to install the software anymore, because we can use the online editor! Found at https://create.arduino.cc/editor, this browser-based IDE means that we don’t have to install the whole software package if we don’t want to. There are some tradeoffs, but it works very well.

1: What does it mean to program the board?

These boards use an Arduino microprocessor board to control the LEDs. All of the animations for the colors of the LEDs are controlled by code that can be changed and uploaded to the Arduino. You can take the code and download the Arduino IDE (Integrated Development Environment – think of it as an application that combines a text editor for the code, a compiler, and an interface to upload the code to the board) and upload your own creation to do whatever you want to the lights. Tons of sample code is in the original program, you can simply rearrange what’s there, or make it only do one pattern, or create your own new patterns.

The Arduino ecosystem works as a series of simple microprocessors that can interpret a very low-level language. We program these microprocessors via the IDE, using a much higher-level C-like language called that is relatively easy to read, and the IDE compiles this code and converts it into a language that the board can understand. The IDE then uploads that code to the board. Because of this, we can’t really get the code that is currently running on an Arduino, so it’s important to keep a copy of the code that we develop.

Step 6: Problems

Occasionally problems creep up.  A big problem popped up in this project when the first board was made.  No matter what was tried, the original design of having the board powered by button cell batteries with an on/off switch couldn’t work properly when finally assembled.  One of the problems wound up being an unacceptable voltage drop that made the battery source incompatible with one of the transistors chosen for the power circuit.  Many changes were tried to get the design to work, but nothing could be figured out without having to redesign the board, and since all of these boards were fabricated already, the decision had to be made to leave out the power circuit, give up on battery-sourced power, and requiring USB.  Very unfortunate, but this is why typically you order a small number of boards and work through some more testing.  Being short on time both because of the school year and free time led to the decision to hope it would work and get all the boards at once, and that gamble did not pay off.  The good news is that it works fine when plugged in to a power source via USB, so at least there is something to show for the project, even if it’s not 100% what the original design was for.  So as a result on the back of the board there are two pads for batteries and several smaller pads for the switch and other components (two transistors, two resistors, two diodes, and a capacitor).

Step 5: Testing

After the boards are made and programmed and any rework happens to fix any bugs, we’re good to go!  Testing along all points of the process is essential.  This last point came back to bite us, as explained next.

Step 4: Fabrication

Once the design is finalized we send that design off to be fabricated.  We use OSH Park for our board fabrication.  It takes a few weeks to produce the boards, and then you’ll get them in the mail.  At the same time we went and ordered all of the other components from Mouser, an electronics supplier.  Then the tedious task of making all of the boards happens.


Step 3: Technical Design

After it works, it needs to be translated into something that can be produced.  We used the Eagle CAD program to build our circuit and lay out our board.  It’s free for personal and educational use, so long as you don’t sell your work product.  There’s a bit of a learning curve, but there are also great tutorials out there. Once you’ve got an idea of the circuit, you create that circuit in the schematic view in Eagle.  Moving to the board view, it takes the schematic and drops those components into a space where you can move everything around to get the placement you want, and then draw the traces, or electrical connections, between all of the components.  Eagle contains a number of great troubleshooting tools that will tell you if your design violates rules that the board fabrication facility might have, and it will let you know if your finished product might not work as expected because things you don’t anticipate touching might accidentally touch.