Digispark BlinkStick Microcontroller Hacking

Digispark BlinkStick USB development board with APA106 LED
Digispark BlinkStick USB development board with APA106 LED

Background

The BlinkStick device came from Kickstarter as an RGB LED connected to a USB port to signal some notification like a new email. With an API you can send it commands to change the LED color and make it, well, blink. The hardware is based off the ATtiny85 8-pin, 8KB AVR microcontroller. Later a company called DigiStump made a similar-but-LED-less USB dongle called Digispark for Arduino development using the same ATtiny85. It was found that by soldering an LED and with some tweaks1 the open-source BlinkStick firmware will run on the Digispark USB development board.

Six years have gone by and the BlinkStick Github repo has gone dormant. The API code repo has also gone cold with only a smattering of forks.

Goal: My project this weekend was to fork both the C firmware and the Java API to make the Digispark do something never done before: execute color patterns on the microcontroller, not the host CPU.

Motivation

My compute cluster has 28 CPUs and each has a USB slot for a status LED. I’m currently using the stock BlinkStick firmware and Digispark clones2.

The problem is that the host CPU must manage the timing and duration of the colors for interesting and colorful patterns. This means my compute cluster is spending many CPU cycles on just looking pretty! My goal is to hack the abandoned firmware to add a mechanism to upload a color pattern so the microcontroller, not the host CPU, executes the color pattern and timings. Let’s push it to its limits.

Hardware

The very first hack is to disable the red power light on the Digispark board. This is an in-circuit light and cannot be disabled with software, but a scalpel does the trick.

Digispark disable the always-on power LED with a scalpel
Digispark disable the always-on power LED with a scalpel

I tested both the APA106 and WS2811 LEDs and the APA106 LEDs were superior in brightness, step, and diffusion. Here is an example distinction between the two at the same brightness.

APA106 vs WS2811 LEDs at same brightness
APA106 vs WS2811 LEDs at same brightness

The completed device looks like this prototype:

Digispark with APA106 RGB LED, 330 ohm resistor, and small capacitor
Digispark with APA106 RGB LED, 330 ohm resistor, and small capacitor

Software Progress

To be honest, I failed in my first 23 attempts to hack the C firmware. I didn’t know anything about interrupt vectors, maintaining registers, and how to deal with 8 bits of data transfer at a time – Java has spoiled me so much. Plus, I have no idea how to debug or trace the microcontroller execution, just trial, error, and patience. I encountered race conditions, lock-ups, and many times the computer wouldn’t recognize my Digispark. I was trying to do something new with this firmware so of course it was tedious and fraught with frustration.

Eventually, I figured out how to create a mutex in the microcontroller world, and came up with a strategy to keep the I/O responsive while the color pattern is executing, as well as upload said color pattern. My hope is that these improvements will be useful to others who want to do neat things with the Digispark and BlinkStick firmware.

Github Project Repos

How it Works

A pattern of up to 62 colors and delays can now be uploaded in a single request. In one API call you can flash the LED red a couple of times with the following byte array payload:

To make more sense, let’s group the bytes like so:

Even more clearly:

You may be wondering how “2” signifies a “20 ms” delay. That is because the range of a byte is 0-255, so I made a judgment call to multiply that by 10 in the backend. That means a delay between 0 and 2.5 seconds is possible per color with a resolution of 10 ms. Also, the 62-color limit is due to a 254-byte v-USB transfer limit. Additional routines could be added to extend this limit, but this is good enough!

Main Loop

Essentially the main loop iterates over the color pattern buffer to set a color then waits for some delay and continues.

Operation

If the Digispark is idle, it will accept a color pattern to upload and execute it immediately. If it is executing a color pattern, it will accept but ignore any new color patterns until it is idle again. This way the I/O never blocks the host CPU. A color inspection can also be performed anytime meaning the host can query the current color even during a color pattern execution.

Also, the Digispark will turn off the led after 60 seconds of inactivity. The reason is that if the status LED is on and green, then the host goes away, the green status LED will be misleading. I want my LEDs off when the host is idle or shutdown.

Installation

The firmware works best with older Micronucleus v1.11. Download the main.hex binary from my repo or compile it with make. Then, upload it to the Digistump with:

Results

Digispark BlinkStick ATtiny85 status LED animation
Digispark BlinkStick ATtiny85 status LED animation
Success: The Digispark now accepts color pattern uploads and can be controlled with the accompanying Java API. Color patterns are now executed on the ATtiny85 thus completely freeing up the host CPU from animation duties.

Application

With twenty-eight of these custom BlinkSticks I can monitor the system resource usage of the processors in my prototype compute cluster.

Compute cluster using 28 custom Digistump BlinkSticks
Compute cluster using 28 custom Digistump BlinkSticks

Notes:

  1. Specifically, changing the I/O pin locations in code.
  2. I ordered a handful of seemingly genuine devices from Amazon and it turned out they are clones (rev 3 models do not exist!), unfortunately, but they work perfectly well for 1/8th the price.