Author Archives: Andrew England

Enginursday: Internet of Terror

via SparkFun: Commerce Blog

In this Enginursday, I've repurposed a project I was using to light fireworks during the Fourth of July. Now I can't really tell you how I did that, but I had an airhorn lying around and figured I could hook that up and have a little fun. The concept is pretty simple: have an ESP32 host up a webpage, click a button on your phone on said webpage, and set off the airhorn! I'm pretty lazy and wanted to do things solderless, so I grabbed a Qwiic Relay and was off to the races. All I had to do was connect my airhorn-battery circuit up to the common and normally open connections on the relay, connect the relay to my ESP32 and add a LiPo to power my ESP32. The whole setup is one of the simplest things I've ever made, but oh boy is it super effective. Check the whole thing out below.

Beeper Setup

Beeper Setup

We just need to add some code for our web server to serve up our button. To do this, we configure our ESP32 as a soft access point, and have it serve up a button for our horn. We then attach our relay functions to our on and off URLs. Lines 69-78 are where our web interface is actually doing actions on the ESP32, so check in here if you want the ESP32 to do some other things on your button presses. Check out lines 17 and 18 to change your WiFi settings to something neat and super secret.

/******************************************************************************
  Horn.ino
  Example for controlling a relay using a webpage served by an ESP32
  by: Rui Santos
  Adapted for horn by: Andy England, SparkFun Electronics
******************************************************************************/

#include <WiFi.h>
#include <Wire.h>
#include "SparkFun_Qwiic_Relay.h"

#define RELAY_ADDR 0x18 // Alternate address 0x19

Qwiic_Relay relay(RELAY_ADDR);

// Replace with your network credentials
const char* ssid     = "HORN";
const char* password = "beepbeep1";

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// Auxiliar variables to store the current output state
String hornState = "off";

void setup() {
  Wire.begin();
  Serial.begin(115200);
  relay.begin();

  relay.singleRelayVersion();

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.softAP(ssid, password);
  server.begin();
}

void loop() {
  WiFiClient client = server.available();   // Listen for incoming clients

  if (client) {                             // If a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            // turns the horn on and off
            if (header.indexOf("GET /horn/on") >= 0) {
              Serial.println("Horn on");
              hornState = "on";
              relay.turnRelayOn();
            } else if (header.indexOf("GET /horn/off") >= 0) {
              Serial.println("Horn off");
              hornState = "off";
              relay.turnRelayOff();
            }

            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #555555;}</style></head>");

            // Web Page Heading
            client.println("<body><h1>The Big Old Beeper</h1>");

            // Display current state, and ON/OFF buttons for our horn
            client.println("<p>Horn - State " + hornState + "</p>");
            // If the hornState is off, it displays the ON button
            if (hornState == "off") {
              client.println("<p><a href=\"/horn/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/horn/off\"><button class=\"button button2\">OFF</button></a></p>");
            }

            client.println("</body></html>");

            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Upload this to your ESP32 and you should see a WiFi hotspot with your chosen SSID pop up when looking for networks on a phone or other device. Go ahead and connect to it. If it's not popping up, open up a Serial connection at 115200 baud to try and see why your ESP32 is unhappy.

WiFi Hotspot

WiFi Hotspot

At this point your phone might tell you that there isn't an internet connection. This is fine, as we only need the ESP32 to serve up its own webpage. Once connected, open up a web browser and navigate to 192.168.4.1 (this is the default IP address, as occasionally the ESP32 has trouble hosting a DNS server). You should see a webpage like the one below.

Webpage

Horn Webpage

Now, hide the thing somewhere and scare some folks!

comments | comment feed

Enginursday: How to Develop a Package for Microsoft MakeCode

via SparkFun: Commerce Blog

Microsoft MakeCode is a quite powerful, block-based coding language useful for anybody trying to learn how to code. However, creating packages for this language is sometimes a little difficult. I've developed quite a few of these packages in the past and figured that the somewhat unclear process could use some documentation for posterity. The below tutorial will get you developing your own MakeCode packages in no time.

New!

How to Create a MakeCode Package for Micro:Bit

April 16, 2019

Learn how to develop code blocks for the Micro:bit in Microsoft MakeCode!

comments | comment feed

Enginursday: Spotify Album Artwork Display

via SparkFun: Commerce Blog

Over the past few weeks, I’ve been working on a few square LED matrices and thus have had glowy square projects on the brain. I had the idea to try and snag my current album artwork off Spotify and display it on some of the matrices. I never had enough prototype matrices to make a nice high resolution display, so I moved to a big old 64x64 matrix. This worked, but there were a few kinks and I got busy again, so I let the project go a little.

My coworker Wes wasn’t satisfied and wanted to see the project finished so he worked out some of the bugs (mainly pinning the task that grabs the JPEG to a separate core to prevent it from interrupting the display). I figured hey, if Wes is so excited, maybe you would be too! Check out the below project tutorial to get started sending the album artwork from what is currently playing to a display.

Live Spotify Album Art Display

Spotify Art Display

comments | comment feed

Enginursday: Glowing Guitar, Part Deux

via SparkFun: Commerce Blog

If you’ve been following my blog posts, you might know what’s coming up with this project. I first posted about using ArtNet to drive pixels here. I then got to work creating an LED-covered guitar in this blog post (I’d suggest checking these out if you haven’t yet, to see the progression of this idea). The next logical step was to combine these two ideas and make a DMX-controlled, glowing guitar! Also, if you’d like to get more familiar, check out my tutorial on DMX below.

New!

Introduction to DMX

November 14, 2018

DMX512 is an industry standard in lighting and stage design, whether it be controlling lights, motors, or lasers, DMX512 has many uses. In this tutorial we’ll cover DMX512 (Digital Multiplex with 512 pieces of information).

That’s a lot more lights than the first DMX demo; any differences?

Why yes, there are! Thanks for asking. A single DMX universe can fit 170 individually addressable LEDs in it (512 channels, three channels required, one for each color, 512 / 3 = 170.66). In that original demo, I was only using 120 lights, which can fit in a single DMX universe. This project uses just over 1500 LEDs, which requires nine universes of DMX data to drive.

The issue here is that DMX is a fire and forget protocol, meaning that it never checks to see if a message is actually received (this is why you shouldn’t control pyrotechnics or stage architecture with DMX). Due to this, DMX packets are occasionally dropped. This means that occasionally, if a packet wasn’t received, the previous packet’s data would be displayed in the dropped packet’s universe, causing one part of the screen to lag. To fix this, I simply have to get rid of old data whenever I write to the screen. In my DMX frame handling function below, we can see that whenever I recieve the last universe, I write to the screen as well as flush my UDP buffer. This ensures that I never write old data to the screen as I flush all of my data away every time I write. I should’ve been doing this in my original demo, but hey, if it ain’t broke don’t fix it, or something, right?

void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data)
{ 
  int offset = (universe - startUniverse) * 170;
  int led;
  // read universe and put into the right part of the display buffer
  for (int i = 0; i < 510; i += 3)
  {
    led = (i / 3) + offset;
    if (led < NUM_LEDS_LOWER)
    {
      ledsLower[led] = CRGB(data[i], data[i + 1], data[i + 2]);
    }
    else if (led < NUM_LEDS_NECK + NUM_LEDS_LOWER)
    {  
      ledsNeck[led - NUM_LEDS_LOWER] = CRGB(data[i], data[i + 1], data[i + 2]);
    }
    else if (led < NUM_LEDS_NECK + NUM_LEDS_LOWER + NUM_LEDS_PLATE)
    {  
      ledsPlate[led - (NUM_LEDS_NECK + NUM_LEDS_LOWER )] = CRGB(data[i], data[i + 1], data[i + 2]);
    }
  }   
  if(universe == 8){
    FastLED.show();
    UdpSend.flush();
  }
}

You’ll also notice that this subroutine reads the universe, and based on that, decides which part of the LED array to add data to.

The other difficult part was actually mapping all of those LEDs to a physical location. This step is painstaking, as you have to create a custom fixture for each small segment of the guitar, then place it in a specific location. This mapping process is different based on the DMX controller software you are using, but you can follow the process for Resolume in my Resolume to ArtNet tutorial. This map is shown in the video below.

comments | comment feed

Enginursday: A Glowing Guitar for Decadon

via SparkFun: Commerce Blog

It’s not every day you get handed a guitar that has only two others of its kind in the world, and it’s an even rarer occasion when the owner trusts you to cover it in LEDs. In order to maintain this trust, I wanted to figure out some way to add the lighting effects that I wanted without affecting the structural integrity of the guitar whatsoever. I didn’t want to drill into or glue onto the guitar due to the lack of an available second chance, so I came up with a plan to sandwich the guitar in between two layers and support my various lighting mechanisms through that.

Slicing the bread

If my guitar is a sandwich, then the bread is the guitar-shaped plexiglass cutouts on either side. To start mocking the shape up, I traced the guitar on a dry erase board with a marker, and measured the rough dimensions of the guitar. I then snapped a picture of this outline and dragged it into Illustrator. Tracing the outline of the guitar didn’t yield perfect results; I probably sliced about 30 “guitars,” moving a line here, nudging a hole there, before I got that wacky shape figured out.

Laser Cutting the Guitar Shape

Laser-cutting the guitar shape

Once I had that sorted, I had to figure out a way to get that bread stuck together. To do this, I added tabs along the outside (in strategic points, which I’ll talk about later). Once I had these tabs attached I was able to sandwich the guitar in between my “slices of bread” and get things attached to the guitar.

It’s not just a screen, right?

I wanted some aspect of this guitar to be new and novel, so I figured I’d add some fiber optic around the edge of the instrument to give it that unique extra touch. Remember how earlier I mentioned that the tabs had been strategically placed? I placed the tabs at inflection points on the guitar, as I’d have to mount the fiber optic to these tabs, and mounting at those inflection points (where a line changes its curvature) will keep the fiber as close as possible to the body of the guitar.

I also wanted an unbroken line of light around the edge of the guitar. To create this effect, I used a double stranded twist of fiber optic along with a clever mounting solution. The issue with lighting fiber optic via LED is that you have to relight the fiber every foot or two depending on the amount of bend and desired brightness. This usually means that every time you relight the fiber optic, you have a dark spot in your line where the lighting module sits. To combat this, I have a double stranded twist of fiber optic; each lighting module sits on a tab in between the “slices of bread” and pumps light into just one strand of the twist, while the other strand passes unbroken over the top. This setup allows me to keep the whole edge lit with seemingly no interruptions.

Now you may notice that the fiber optic runs down the neck of the guitar and you might be thinking, “Hey Andy, I can’t play guitar with that in the way!” and to that I say yeah, you know what, you’re probably right. Due to that this little feature might not be on the final product, but I’m gonna test it with the artist and see if it’s playable, and if not, hey it looks pretty sweet for now.

Mocking up a pixel layout

I didn’t want to over- or under-order my pixels, and I also wanted to have an idea of how I would transmit data to the array, so I wanted to mock up the array before laying pixels down. To do this, I created a mock-up of an SK6812 skinny pixel strip, printed a whole bunch out, then carefully cut and laid them down on a cardboard cutout of the guitar shape. I then numbered the pixels and decided how I’d send all the data (I eventually settled on three strips of LEDs).

Skinny LED RGB Strip - Addressable, 1m, 144LEDs (SK6812)

Skinny LED RGB Strip - Addressable, 1m, 144LEDs (SK6812)

COM-14732
$29.95

Once I had my rough mock-up completed I ordered around 1600 SK6812 LEDs and set about cutting and soldering the strips onto one of the plexiglass “slices of bread.” Note that power does not need to necessarily follow a particular direction; you can connect the power/ground of any strip to the power/ground on any other part of the next strip. This is unlike data, which must zig-zag down the array so data reaches every pixel.

Ground first! Make sure to connect the ground of your LEDs before hooking up any other connections.

Help me, my pixels are flickering

That’s not enough info, you’re gonna need to tell me a little bit more. Flickering is usually caused by a loose connection – you can usually tell which connection is loose upon visual inspection of your flicker.

  • Pixels only glow a dull red, and change with the rest of the pattern - Inspect your ground connections; your circuit is powered but not complete.
  • Pixels intermittently change with the pattern - Inspect your data connections; you probably have a loose data line.
  • Pixels just go berserk - Your circuit is probably grounded, but not grounded well to the microcontroller or some of the strips near it.
  • Intermittent white flash every 5-10 seconds - Are you using 3.3V logic? Either convert it up to 5V logic or you can (cheat) turn the supply of your LEDs down to around 4.3V, which will put 3.3V within the logic levels accepted by the LEDs.

That’s a lot of LEDs, mind elaborating on all that wattage in the cottage?

This project is a pretty large one, involving a TON of SK6812 LEDs. In fact, at full white, I could hypothetically be yanking nearly 500W. This being said, these LEDs are actually a little painful to look at when you make them vomit as many photons as possible, so for a screen application, it’s good to bring them down to about 25 percent brightness (I did all my debugging at 12.5 percent to avoid blindness) to allow for a sane amount of light to enter the retinas of your audience. However, the LEDs in the fiber optic should run at full brightness as the fiber diffuses the bare LED quite well.

Mean Well Switching Power Supply - 5VDC, 20A

Mean Well Switching Power Supply - 5VDC, 20A

TOL-14098
$25.95

Speaking of diffusion, nobody likes looking at bare LEDs right? Right. In order to add a little bit of diffusion and also clean up the look of the project, I snagged this neat, heat-activated fabric called Wonderflex and formed it over my custom screen once I made sure all of my solder joints were properly done. This step was quite a lot easier than I thought it would be, just 180°C from my heat gun and bend. The finished product looks a bit better than bare LEDs.

This test code looks like Juicy Fruit

I got a little bit sick of staring at the rainbow, so I figured I’d try and generate some other good-looking gradients. Inspired by Felipe Pantone’s artwork in which he stacks color waves on top of one another, I decided to take a similar approach. What I did first was create a sine wave for red, green and blue, then I randomized the amplitude and frequency of each wave and stacked them on top of each other. I reshuffled my frequencies and amplitudes once all combinations of the waves had been displayed. The result is quite stunning and generates a bunch of natural-looking gradients that I would never have come up with on my own. Pipe this code into your own LED strips and let me know what you think!

#define FASTLED_ALLOW_INTERRUPTS 0
#include "FastLED.h"

// How many leds in your strip?
#define NUM_LEDS_LOWER 315
#define NUM_LEDS_NECK 600
#define NUM_LEDS_PLATE 672
#define NUM_LEDS_RIM 24
#define TOTAL_LEDS  NUM_LEDS_LOWER + NUM_LEDS_PLATE + NUM_LEDS_NECK

#define DATA_PIN 23
#define DATA_PIN_2 18
#define DATA_PIN_3 13

// Define the array of leds
CRGB ledsLower[NUM_LEDS_LOWER];
CRGB ledsNeck[NUM_LEDS_NECK];
CRGB ledsPlate[NUM_LEDS_PLATE];

uint8_t rotation = 0;
uint8_t offset;
float newRedAmplitude = 3;
float newGreenAmplitude = 4;
float newBlueAmplitude = 5;
float oldRedAmplitude = 3;
float oldGreenAmplitude = 4;
float oldBlueAmplitude = 5;
int newRedFrequency = 3;
int newGreenFrequency = 4;
int newBlueFrequency = 5;
int oldRedFrequency = 3;
int oldGreenFrequency = 4;
int oldBlueFrequency = 5;

void setup() { 
  Serial.begin(115200);
  Serial.println("resetting");
  pinMode(DATA_PIN, OUTPUT);
  pinMode(DATA_PIN_2, OUTPUT);
  pinMode(DATA_PIN_3, OUTPUT);
  LEDS.addLeds<WS2812,DATA_PIN,GRB>(ledsLower, NUM_LEDS_LOWER);
  LEDS.addLeds<WS2812,DATA_PIN_2,GRB>(ledsNeck, NUM_LEDS_NECK);
  LEDS.addLeds<WS2812,DATA_PIN_3,GRB>(ledsPlate, NUM_LEDS_PLATE);
  FastLED.setBrightness(64);
  randomSeed(analogRead(4));
}

void stackSines ()
{
  for (int strip = 0; strip < 3; strip++)
  {
    uint16_t NUM_LEDS;
    switch (strip)
    {
      case 0:
      NUM_LEDS = NUM_LEDS_LOWER;
      break;
      case 1:
      NUM_LEDS = NUM_LEDS_NECK;
      break;
      case 2:
      NUM_LEDS = NUM_LEDS_PLATE;
      break;
    }
    for (uint16_t ledPosition = 0; ledPosition < NUM_LEDS; ledPosition++)
    {
      offset = (ledPosition / 4) + rotation;
      uint8_t redFrequency = cubicwave8(offset * newRedFrequency);
      uint8_t greenFrequency = cubicwave8(offset * newGreenFrequency);
      uint8_t blueFrequency = cubicwave8(offset * newBlueFrequency);
      switch (strip)
      {
        case 0:
        ledsLower[ledPosition] = CRGB(newRedAmplitude * redFrequency, newGreenAmplitude * greenFrequency, newBlueAmplitude * blueFrequency);
        break;
        case 1:
        ledsNeck[ledPosition] = CRGB(newRedAmplitude * redFrequency, newGreenAmplitude * greenFrequency, newBlueAmplitude * blueFrequency);
        break;
        case 2:
        ledsPlate[ledPosition] = CRGB(newRedAmplitude * redFrequency, newGreenAmplitude * greenFrequency,           newBlueAmplitude * blueFrequency);
        break;
      }
      if (offset == 0 && ledPosition == 0)
      {
        frequencyShuffler();
      }
    }
  }
  FastLED.show();
  rotation++;
  delay(10);
}

void frequencyShuffler()
{
  uint16_t shift = 0;
  float newAmplitudeVal;
  int newFrequencyVal;
  for (int i = 0; i < 3; i++)
  {
    newAmplitudeVal = random(0, 255) / 255.0;
    newFrequencyVal = random(2, 14);
    switch (i)
      {
      case 0:
      newRedAmplitude = newAmplitudeVal;
      newRedFrequency = newFrequencyVal;
      break;
      case 1:
      newGreenAmplitude = newAmplitudeVal;
      newGreenFrequency = newFrequencyVal;
      break;
      case 2:
      newBlueAmplitude = newAmplitudeVal;
      newBlueFrequency = newFrequencyVal;
      break;
    }
  }
}

Going further

Any project always has room for improvement, and with this one there are a couple things I’d like to see. The first thing would be a tighter pixel density for the face of the guitar. The second would be to continuously shuffle the frequencies and amplitudes such that the waves are constantly shifting their shapes and sizes. The next effect I was going to add was varying speeds and directions on each of the waves to allow even more variability. Getting all of these things implemented would make a seriously good-looking effect for LED strips. If you have suggestions for wacky color patterns or functions you like to run on your LEDs let me know in the comments below.

comments | comment feed

Wedginursday: Frame Rates and Funky Colors

via SparkFun: Commerce Blog

I’ve been experimenting with the APA102 LED lately due to their ability to operate at extraordinarily high frequencies. I want to see if I can use these for a persistence of vision (POV) display, and if I can, I’d like to see just how many LEDs I can run at a POV capable frame rate (typically something around 1000 FPS should work, but for wider displays where LEDs are swept across a larger space, higher frame rates are desired).

What is Persistence of Vision?

Persistence of Vision is a neat little phenomena that exists thanks to your brain and afterimages. An afterimage is an image that stays in your vision after you’ve looked away from the original visual stimuli. If you’ve ever gotten a picture taken of you with a bright flash or played with sparklers on the 4th of July, you’ll know exactly what I’m talking about. This sort of effect occurs because sight is a photochemical process. In other words, light interacts with chemicals, your cells see this and send signals along the optic nerves to your brain based on these chemical signals. When you see something (especially something bright) the chemicals in your eyes become depleted. This causes an afterimage from the object even though you aren’t necessarily looking at it anymore (don’t stare at the sun, kids).

Thanks to these afterimages and this effect, we’re able to perceive things like video, which is a series of photos flashed just quickly enough to blur together. This tends to be around 30 FPS for most video, although when things are moving quickly (like in sports or a fast-paced movie), frame rates as high as 60 FPS might be required for a smooth image.

Taking this into (design) consideration

Since these afterimages “stay” in one spot in our vision, by sweeping pixels through the air we can use LEDs to paint images seemingly in thin air. The fun with these displays is that you need to run your lights at a high refresh rate so you can have a high resolution as you sweep your pixels through the air. A higher frame rate means the pixel can be swept through the air faster. The larger a POV display gets, the faster the outer pixels will be swept through the air, so a higher frame rate translates to bigger displays. However, a bigger display means more LEDs, which in turn means a slower frame rate.

Fast Hardware

In order to accomplish an ultra-fast frame rate, we’ll need some specific hardware. I went with the APA102 LED due to the fact that, unlike some other LEDs, like the WS2812, it has an external clock. Since the clock is provided by the microcontroller it’s far easier to synchronize all of the LEDs and the controller, allowing for longer, faster strings. This has enabled various makers to push data cleanly through 1000+ pixel strings at speeds around 30 MHz.

Now we need something that can run our SPI bus at 30 MHz and still have clock cycles to chew on for any animations we want to run. For this I chose the Teensy, as it can run its SPI bus that fast, and I also discovered a little trick to get more out of fewer pins on the Teensy. I’m also using the FastLED library, which is devoted to running lights on as few clock cycles as possible.

Teensy 3.6

DEV-14057
$29.25
12

I Cheated

The APA102 has a communication protocol that can be taken advantage of to control four LED strips with only four data pins (you should normally need eight). In order to accomplish this, I wired my four strips according to the table below.

StripDataClock
1713
2714
31113
41114

You can see all four pins have two wires connected to them, each wire being from a different strip. This setup takes advantage of the fact that when strip one is being driven, the data won’t affect strip two due to the lack of a clock signal for strip two, while the clock won’t affect strip three due to the lack of data for strip three. This sort of logic repeats for the rest of the strips, allowing our Teensy to be able to juggle four LED strips around on four data pins – pretty neat! Check out the below oscilloscope capture of pins 7 (yellow) and 13 (blue) to get more of an idea of what’s going on.

Oscilloscope Capture

This is also a great way to look at frame rate, which in this setup (a mere 60 LEDs) is hitting a whopping 10,000 FPS, which is overkill for most applications, but it’s also not many LEDs. Increasing my number of LEDs to 1,000 brought me down to around 770 FPS, which is about the minimum for what I’d like for POV applications. A 500-LED display would put me at 1,500 FPS, the sweet spot for what I’d like to do.

Hypothetically, you’d be able to get 24,000 LEDs (four strings of 6,000 LEDs) running at around 30 FPS, but I don’t know how clean the signal will look after being sent through 6,000 lights, I don’t have that many LEDs lying around for a hypothetical. Also, powering something that size would be a neat little problem in itself.

Other Applications

After some initial testing I settled on a frame rate of 7,650 FPS to do some testing for some motion-activated color gradients. What I did was take a color gradient and cycle through at a speed high enough to run through the whole 255-color gradient 30 times every second without losing any resolution in the gradient. This flashes through all of the colors fast enough that if you hold the strip still, the afterimages stack on top of one another and you see an average of all the colors (the rainbow will show up white). Moving the LED around as it does this will allow you to see the rainbow, as now the afterimages are all in different locations instead of stacked on top of one another. I took some long exposure photos to visualize the effect and they came out great! You can actually see each individual frame refresh if you look closely at the image. The LEDs almost look like little stacks of colored coins, where each “coin” is a frame.

Long Exposure

comments | comment feed