NeoPixel Ring Mod – Part 2

NeoPixel Ring Mod – Part 2

In my previous entry on the NeoPixel ring mod, I detailed my experimentation on finding a good mix between LED “bling” and Crazyflie performance. I’d arrived at Adafruit’s 12x LED NeoPixel Ring as the sweet spot. Before making the mount permanent with double-stick tape and solder, I wanted to make sure I could actually get the ring to light up! I figured that wouldn’t be too hard, since Bitcraze had already added the code to run it in a neopixel_dev branch in their crazyflie-firmware Github repository. The main thing I’d need to attend to was re-purposing the code for 12x LEDs (it was originally written for 16x LEDs) and then building and flashing custom firmware using the new code. Since I’d already set up a mini development environment for the firmware on my Mac, building and flashing should be easy. Retooling the NeoPixel firmware code hopefully would be as well, and then I could do a proper smoke test to see it light up (and hopefully not smoke)!
compiling firmware
First off, I simply cloned the neopixel_dev branch and tried to build it as is.

git clone --branch neopixel_dev --single-branch
cd crazyflie-firmware
git branch

It compiled cleanly and a new NeoPixel Ring enabled cflie.bin was born. Using the Python CFClient and the radio bootloader functionality, I flashed the firmware to the Crazyflie and that was successful as well. I wanted to to do a prototype wire up and see if I could even get the NeoPixel Ring lit. I used my x16 LED ring and a few long pieces of 26AWG stranded copper wire. I would have liked to use a smaller gauge but I didn’t have any handy so 26AWG was going to have to do.

Crazyflie NeoPixel Ring wiringI found a graphic on the Bitcraze blog entry about the NeoPixel Ring mod. Using that, I was able to find the right holes on the expansion port for the three wires necessary to drive the NeoPixel ring (positive, ground, and data). The picture was much easier than trying to figure out what I needed from the expansion header documentation on the Bitcraze wiki (remember, I’m a hardware/electronics layman – an electronics jockey would probably love the header documentation). After stripping the insulation off both the wire ends, I twisted the strands together and simply stuck them through the holes in the expansion header and then through the corresponding holes in the NeoPixel ring. Python client with ring parametersWith that done, I turned on my Crazyflie and connected to it with the Python client. I was super happy when, on the “Parameters” tab, I saw values for ring.effect and ring.neffect! The ‘effect’ parameter is read/write and allows you set what lighting effect to show. The ‘neffect’ is read-only and holds the total number of effects available. So far this was much easier than I thought it was going to be! My next step was to alter the firmware code a bit to make it work with 12x LEDs rather than the 16 it was written for. I jumped into the Crazyflie firmware code and quickly found what I needed to tweak…

In the file neopixelring.c in modules/src in the crazyflie-firmware repository is all the code for lighting effects.The effects are simply “function” blocks and the rest of the file is the mechanism for loading and executing the effects code. The number 16 is littered about in a lot of the functions and there are a number of color arrays with 16 elements in them. The first thing I did was find/replace all instance of 16 with 12 (but later just created a #DEFINE for MAX_PIXELS set to 12). The next thing I did was reduce the arrays to 12 elements from 16. The arrays didn’t necessarily need adjusting. I could have left them with 16 elements but the smoothness of the effects were impacted and there’s also no need to waste the storage space on array elements that go unused. A couple effects required a little more thought (the color wheel and dynamic tilt effects in particular). The color wheel was just a matter of rethinking what the color wheel should look like with four fewer pixels but the dynamic tilt was a little tougher. For that one I had to figure out the orientation of the physical ring compared to what was implemented. I played around with more int he code and created a couple more effects. I won’t go into detail as they’re details best left to discover yourself. The following are a few code snippets but you can find all my changes on my GitHub fork

#define MAX_PIXELS 12

static const uint8_t cw_whiteRing[][3] = {BLACK, BLACK, BLACK, BLACK, BLACK,
                                          {1,1,1}, {2,2,2}, {4,4,4}, {8,8,8}, 
                                          {16,16,16}, {32, 32, 32}, {40, 40, 40}

static void whiteSpinEffect(uint8_t buffer[][3], bool reset)
  int i;
  uint8_t temp[3];
  if (reset)
    for (i=0; i<MAX_PIXELS; i++) {
      COPY_COLOR(buffer[i], cw_whiteRing[i]);
    // Runs Clockwise
    COPY_COLOR(temp, buffer[11]);
    for (i=11; i>0; i--) {
        COPY_COLOR(buffer[i], buffer[i-1]);
    COPY_COLOR(buffer[0], temp);  


pixel orientationFor the dynamic tilt effect (my favorite for sure!), I had to adjust the position of the front, back, right, and left “blocks” of pixels since the 12x ring has three of each instead of four of each (you can see exactly what I did in my GitHub fork). This also meant I had to understand which pixel was the first to get assigned a color value (0 in the software arrays). I planned to put that one at the very front of the crazyflie (front being the M1 motor mount since I typically fly in + configuration). I loosely wired up my 12x ring this time, then I re-flashed firmware with an effect that just set the first four pixels (array elements 0, 1, 2, and 3) to white, red, green, and blue respectively. I noted where they were in comparison to the data “in” terminal on the ring. As it turned out, pixel 0 (the first one – white) was just to the left of the “in” terminal and they followed in succession clockwise. Now that I knew the ring actually worked, and I knew the positions of the pixels, I was ready for a more “permanent” mount.
pixel guide
In my next installment, I’ll detail how I mounted the ring and later I’ll explain some of the changes I had to make in CFClient to support changing the effects via my controller. This later exercise was one of great discovery because I didn’t find any code that already handled it. Most of what I did was guesswork but guesses that worked!

My GitHub fork is here for reference…

Comments are closed.