Author Archives: Andrew England

Enginursday: Multiple I2C Ports on ESP32

via SparkFun: Commerce Blog

For as long as I've been at SparkFun (longer actually, this project started back at school), I've been working on a fiber optic light suit. The idea originally came about because I was frustrated with the lack of controllability of EL wire. It's only monochrome and, as far as I can tell, most inverters don't have the required duty cycle for PWM. I wanted to have glowing wires all over me but didn't know where to look.

A friend who was also interested in the idea bought me a roll of some thin fiber optic from a supplier in Texas, and I was off to the races, hot gluing bundles of fiber optic and simple 5mm LEDs into Starbucks straws (my part-time college job) and putting them all over my arms.

Version 2 of the Light Suit at  Pretty Lights show in 2017

Version two of the Light Suit at a Pretty Lights show in 2017

I've come a very long way since then. Four versions later, I've moved from "dumb" LEDs in straws and bulky PWM controllers running on a relatively slow Arduino Pro Mega that I had left over from a digital electronics class, to shiny, custom PCBs with SMD APA102s, 3D-printed mounts, an ESP32 Thing Plus and even I2C based finger-bend sensors that eventually became a SparkFun product. In versions three and four, I made the move to add legs and a torso to this glowing suit, adding a whole host of new problems. After version three had issues staying plugged in, I took a step back and collected my thoughts before starting again.

I finally gathered the motivation and time to pick this project back up and figured I'd tackle my problems getting my six I2C gesture sensors to pull some readings, four of which are the ADS1015, ADC-to-I2C-based Qwiic Flex Glove Controller, and read how bent all of my fingers (excluding my thumbs) are. The other two sensors in the feet are also ADS1015-based boards that attach to force sensors; this enables me to see how my weight is balanced.

Weight Sensing Insole

Weight-sensing insole

The ADS1015 has a hardware configurable address; tie the ADR pin to GND, 3V3, SDA, or SCL to select one of four available addresses. If you're a math genius you'll realize we have more sensors than addresses. That's a problem our trusty ESP32 can solve. We'll simply need to create an I2C bus for each side - this way we can have each side be a mirror image of the other in code. To do this, we first need to include a few libraries and create our I2C buses.

#include <SparkFun_ADS1015_Arduino_Library.h>
#include <Wire.h>

TwoWire leftBus = TwoWire(0);
TwoWire rightBus = TwoWire(1);

We'll then say which addresses we want our sensors to be on as well as create the objects for our sensors themselves, since we have two buses, we'll only need to use 3 addresses.

/***Addresses for Different Parts of Limb***/
#define PINKY 0x48
#define INDEX 0x4A
#define FOOT 0x4B

/***ADS1015 Objects***/
ADS1015 leftPinky;
ADS1015 leftIndex;
ADS1015 leftFoot;
ADS1015 rightPinky;
ADS1015 rightIndex;
ADS1015 rightFoot;

In our setup loop, we initialize some Serial output to see what's going on, we then initialize our left and right buses on two different sets of pins. I also use 100 kHz as I've got some pretty long wires, which hinders I2C communication. We then add our gesture sensors to their respective buses using their respective addresses. No need for an I2C Mux today! Note that I also change the gain of my sensors; this is specific to my project and sensors, so you won't need to do this for your accelerometer or other I2C device.

void setup() {
  /***Debug Output***/

  /***Initialize I2C Devices***/
  leftBus.begin(23, 22, 100000);
  rightBus.begin(16, 17, 100000);
  leftPinky.begin(PINKY, leftBus);
  leftIndex.begin(INDEX, leftBus);
  leftFoot.begin(FOOT, leftBus);
  rightPinky.begin(PINKY, rightBus);
  rightIndex.begin(INDEX, rightBus);
  rightFoot.begin(FOOT, rightBus);

  /***Change Gain***/

My main loop() is as simple as printing the values out over Serial!

void loop() {
    for (int channel = 0; channel < 2; channel++)
        Serial.print(": ");
        Serial.print(": ");
        Serial.print(": ");
        Serial.print(": ");
    for (int channel = 0; channel < 4; channel ++)
        Serial.print(": ");
        Serial.print(": ");

And here's the output below, notice how all of the feet values are resting close to 0, as the circuit the board they are on doesn't include the op-amp circuit.

Serial Output from 4 Limbs

Serial output from all four limbs

comments | comment feed

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.

  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() {


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

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 =;             // 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("Connection: close");

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

            // 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>");


            // The HTTP response ends with another blank line
            // Break out of the while loop
          } 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
    Serial.println("Client disconnected.");

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 (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.


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.


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.


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]);
      ledsPlate[led - (NUM_LEDS_NECK + NUM_LEDS_LOWER )] = CRGB(data[i], data[i + 1], data[i + 2]);
  if(universe == 8){;

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)


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


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!

#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 DATA_PIN 23
#define DATA_PIN_2 18
#define DATA_PIN_3 13

// Define the array of leds

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() { 
  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);

void stackSines ()
  for (int strip = 0; strip < 3; strip++)
    uint16_t NUM_LEDS;
    switch (strip)
      case 0:
      case 1:
      case 2:
    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);
        case 1:
        ledsNeck[ledPosition] = CRGB(newRedAmplitude * redFrequency, newGreenAmplitude * greenFrequency, newBlueAmplitude * blueFrequency);
        case 2:
        ledsPlate[ledPosition] = CRGB(newRedAmplitude * redFrequency, newGreenAmplitude * greenFrequency,           newBlueAmplitude * blueFrequency);
      if (offset == 0 && ledPosition == 0)

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;
      case 1:
      newGreenAmplitude = newAmplitudeVal;
      newGreenFrequency = newFrequencyVal;
      case 2:
      newBlueAmplitude = newAmplitudeVal;
      newBlueFrequency = newFrequencyVal;

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