Browsing posts in: Electronics

Skiing penguins build log retrospective

My skiing penguins project has been up and animating for a few weeks, and I thought I’d write down the process for others so that they can learn from what I did. And also so I can remember what I did and why. There are some other blog posts that talk about some of the specifics for the sequence controller I built.

First off, there’s a video here that shows the final result.

The my initial idea was to do something with LEDs and single frame animation; see some of the animations that were done at ZooLights at Pt. Defiance many years ago was what got me into this hobby. I had a big garage roof that was unutilized, and after some discussion with my wife we decided that skiing penguins was a good place to start.

CAD

 I started looking around for online penguin designs to use as a starting point and found a couple. Then I started up Fusion 360 and created a new project.

I design (and sell) LED ornament kits, so I’m used to doing these designs. Here’s a video that shows the technique that I use to space the LEDs out evenly along an outline; that is what I used for all of the penguins. Here’s a in-process look at one of the designs:

image

and a final one:

image

The final designs were too big to cut in my laser cutter (glowforge), so I had to break them into two pieces. The puzzle-piece line gives me two individual pieces to cut that can easily be put together again.

The animation has 15 frames, which meant 29 individual pieces to cut.

Cutting

I figured out what sizes of plexiglass I needed and bought the plexiglass from my local TAP plastics; it was about $90 worth. I didn’t want to spend a lot of time splitting pieces on my table saw, so I defeated the door interlock on my Glowforge for the larger pieces:

IMG_9644

You really shouldn’t do this if you don’t have laser safety glasses designed for your particular laser’s frequency. In this case, I wore my normal safety glasses and closed off as much of the opening as possible; I now have a nice set of dedicated laser safety glasses. Don’t do this unless you understand the risks.

Here’s all the cut pieces stacked up:

IMG_9647

This is a penguin prototype:

IMG_9651

This was actually constructed earlier; it was used to determine size, and we decided to go about 25% larger. Thankfully, Fusion made this fairly easy to do.

Here’s the first production frame:

IMG_9654

You can see the glue residue along the joining line. At the left and right sides there are short connectors that overlap the joint and provide much needed stiffening; they are on both sides. The squares with holes are spacers; the penguins will mount to wood supports and the spacers hold the wood away from the wiring that will be on the back. Zoom in to see the fine details.

Here’s the pile of penguins all glued together and ready for LEDs.

IMG_9656

First light on the first frame:

IMG_9659

Green was planned for the body outline rather than the white in the prototype but I didn’t have enough green LEDs at the time. The LEDs are brightness matched to look similar to human eyesight though the blues look too bright to the camera. Only 13 more to go!

Here’s what the penguin looks like with the leds off:

IMG_9660

Sorry about the messy and distracting background. For each section of LEDs, there are two sets of bare copper; one that is connected to 12V and one that is connected to ground. Leds are grouped based on their voltages; green and blue run in groups of 2 while reds run in groups of 4. At this point I realized that my ski poles had 9 leds which means that I had two groups of 4 and one individual LED, which was a pain. Each group has the appropriate resistor to set brightness. There are 70-odd LEDs in this frame, so figure something near to 200 solder joints.

Here’s a picture of the workbench mess:

IMG_9662

That’s not that bad except there is more mess here:

IMG_9663

and here:

IMG_9664

To waterproof all the wiring, I took the penguins outside and hit them very heavily with clear acrylic spray. I’ll know how well that worked when I take them down:

IMG_9665

Here’s a view of them drying. They have waterproof power connectors attached and have frame numbers marked on them.

And then they were each mounted on short pieces of 2×3 wood which would have been painted if I had time; maybe I’ll do that when I pull them down after they have dried out. You can see the wiring quite well in this shot, and we can see that it’s frame “J”, or the 10th frame.

IMG_9670

The next step was to build the wiring harness. I did a diagram of the expected layout in Visio:

image

From that, I went out into my driveway, took a couple of tape measures, and laid out what I needed. The controller was planned to be under the roof where “K” is, so I would run wire to that point and then leave an extra 10’. I unfortunately don’t have any pictures of this process; I was racing the weather.

Basically, I would start at “A” in the diagram, leave a couple of feet of slack cable, run it up to D, across the top, and then down to K and leave 10’ at that end. Each cable gets an adhesive label with the circuit letter at each end, and those will later be covered with clear packing tape to waterproof them. Continue the process with each cable all the way through K. The cable is 22-gauge alarm cable; I have used twinlead in the past and I have to say that this cable was a huge upgrade in terms of ease of use, and at $31 for 500’ is was pretty cheap. I did calculations on the voltage drop and decided that it wasn’t too bad (about 7% IIRC).

Once I had all the cables, they got bunded together with wire ties at every branching location and then additional wire ties to make the harness easy to handle. I got it all done just as the rain came down for real, and headed inside. Each penguin location got the other half of the waterproof connector soldered on and then covered in heatshrink tubuing, and the controller ends got stripped to be connected to the controller. Here’s the final roof harness:

 IMG_9671

The penguins got installed on the roof with various arrangements of wood to support them and were plugged into the harness. I then spent a day or two figuring out how to do the penguins in front of the house; they are supported on 1/2” metal EMT tubing. I also created a separate harness for those penguins (one in the air, one crashed in the tree, and then a small one where the one in the tree lost his poles and skis) using the same process.

I finished building the controller:

IMG_9672

Nearest to us is the ESP32 controller board that runs the animation software, and behind it are two custom 8-channel MOSFET switching boards. Attached to the back MOSFET board is a series of LEDs used for debugging.

The ESP runs custom software that drives the ESP32’s 16-channel PWM hardware. The ESP32 is ridiculously full-featured for the price. One of my goals for the project was to *not* have to pull the controllers out of their installed location to update the animation, so I created a really rudimentary web-based IDE and an animation language:

image

This is the current active view from the ESP running the penguins as I sit here and write. The left textbox shows the code and the right one shows errors if there are any. I wrote the animation outline and programmed it in, and then took my laptop to the garage and we’d watch the animation and I’d tweak it as necessary, it took us about 10 minutes to get what we wanted, and I would have spent more than that on a single iteration of “unplug the controller, take it downstairs, plug it in, modify the software, compile it, upload a new version, take it outside, plug it back in, and see if it worked”. That worked very well.

I’m calling the language “Dim”, because it’s good at dimming things and not very smart. In the code “DI” means drive a specific channel to a specific brightness over a specific cycle count (each cycle is about 10mS, so it runs at 100 Hz), and D lets you specify more than one operation to occur at once during the following “A” (animate) command. The language does have for loops but is desperately in need of functions/methods for this usage; I have those in a newer version.

Here’s a bit of code running on a second instance of the controller that flashes 5 of the ornaments I make in a random pattern:

channel=2
FOR count 1:100
   D(50, channel, 0)
   channel=R(1:6)
   DI(30, channel, 1)
ENDFOR  
      

That took about 5 minutes to write.

Finally, here’s a daytime tour of the installation which shows the penguins mounted on the roof and in front of the garage and the controller board with terminal strips.



A self-expanding ESP32 PWM board…

I’ve been working on a little ESP32 expansion board/shield for an LED project I’ve been working on. One of the nice things about the ESP32 is that it has a peripheral known as “LED control” that provides 16 independent channels of PWM for controlling LED brightness, and my project uses that capability.

One of my projects is going to require all 16 channels, so I wanted to do a board that would support 16 channels, but I also wanted a version of the board that would only support 8 channels. I started with the 8 channel board and figured out a way to build a single board that would support 8 channels and also function as an expansion board to add the other 8 channels. I thought the approach was interesting enough to share it here…

Let’s start with a picture of the board:

image

I’m using the 20-pin development board, and it turns out that of the 16 channels supported by the LED control peripheral, half of them are on each set of pins. I started doing an 8-channel version of the controller; you can see the primary pins on the right side of the board that come into the center of the board and then head down into the MOSFET region of the board. That design was fairly simple to do.

Then I needed a way to do something with the 8 channels on the other set of pins. At that point, I realized that if I could use the same board to get the other 8 channels if I flipped the board over:

image

On the primary board, the pins for channels 9-16 on the left side of the board are connected to a header. The ESP32 will be connected to headers on the top side of the primary board, and then we will add a header to the primary board expansion pins – pins 9-16 – on the underside of the primary board.

We will then take a second board and flip it upside down. That puts the header pins that are connected to the MOSFETs on the left side of layout directly under the header on the primary board connected to the expansion pins, so we can just put a complementary header on the expansion board and just stack them together.

Here’s what it looks like in the real world. This is the primary board with headers for the ESP-32 on the left and MOSFETs on the right. That is set up for 8-channel mode.IMG_9631

To enable 16-channel mode, we flip this board over and add some headers. The top header connects to pins 9-16 from the ESP32, and then there’s a single pin on the bottom which connects to ground (if I do a future version I’ll add an extra pin on the left) to provide a bit more support.
IMG_9632

We then take a second board and set it up as an expansion board by adding the complementary headers to it.

IMG_9633

We can then finally stack the expansion board on the back of the primary board and add the ESP32:

IMG_9634

That gives us 16 outputs.

Here’s a short video demo of the controller:

And a second video that show my first project using it:


Sequence Controller Part 3–Board design and MOSFET testing…

Boards are in the house!

IMG_9608

JLCPCB did a nice job, and the boards look fine. Except:

IMG_9609

Yeah. Those pins are beautifully aligned a very precise 0.1” from where they are supposed to be…

Pro tip: Print out your design and put your components on it so that you can check the design.

Meta pro tip: Follow your pro tips.

Anyway, that’s not the only problem; it turns out that the power and LED parts of the connector are right underneath the end of the board, so you can’t use a normal header on them (you could use a right-angle one if you wanted), so I did a new revision of the board with 1.0” rather than 1.1” for the ESP and extended the board so the connectors are out on the end. That’s on the slow ship from China right now.

Then I did a bit of bodging with some long-tail female headers so I could still do testing.

IMG_9611

Then I put a header for the LEDs and carefully soldered 8 resistors and LEDs to the output pins, so that I have an 8-channel version available for writing software.

IMG_9612

The MOSFETs are pretty darn small, but soldering them was mostly okay. I didn’t bother doing a stencil for this rev so I could reflow, but I will likely do that for the next version.

I have not yet tested what I think is the coolest part of the design; the board is both a main board and an expander board; you can connected a second version of the board on the back of the one with the ESP32 connected to it, and it will get you channels 9-16.

Here’s a quick video of the current state:



Sequence controller test from Eric Gunnerson on Vimeo.

It’s doing a “breathe” on all 8 LEDs with varying timespans for the delay action.

This is the 6th or 7th time that I’ve written sequencing software; there was a Motorola HC11 version, two AVR versions with AC dimming, a 4-channel chaser, and a couple of WS2812 versions.

They were all very simple; take the current state of all the output and drive the outputs to a new state over a given period of time. That works fine, but writing the animation can be annoying and it’s not very compact. This time I wanted to do something different and more elegant:

Here’s my spec:

     IMG_9614

That means ‘loop variable %A from 0 to 7’, and then execute a 100 cycle (1 second) dim of channel %A from its current state to 1.0 (full bright), and then do the same dim back down to zero.

I also wanted to write the vast majority of the code on my desktop, so I took a break and wrote three blog posts about how I do that. It’s basically compile-time dependency replacement with unit tests mostly written using TDD.

Then it was off to writing a *lot* of test code and a lot of classes; 18 difference source files, only two of which are ESP specific at this point. And 15 test classes to drive the tests. It mostly worked great, I did 95% of my coding and only had once latent bug that I had to track down on the ESP32. It was weird one that turned out to have very random behavior. I suspected it was uninitialized data, and that turned out to be mostly right; two subsequent calls to a method used the same stack and I forgot that strncpy doesn’t copy a null. But it all works now. Here’s the code the video is running:

$1$LOOP %B 100:10:-10
    $1$LOOP %A 0:7
        $%B$D%A,1.0
        $%B$D%A,0.0
    $1$ENDLOOP
$1$ENDLOOP”

Variable %B is used to change the cycle count for the operations from 100 to 10 in steps of 10, and then the inner loop cycles through the 8 different outputs. Everything works great.

The code all lives here if you want to see the actual code or a more realistic testing example.

Next steps:

  1. Wireless implementation to do the connection to the ESP
  2. Save and load of the animation
  3. Web interface to edit the animation.
  4. Change the language to move the cycle count into the “dim” command, as it’s not necessary for the loop commands.
  5. Build a second board to test channels 9-16.





Sequence Controller Part 3–Board design and MOSFET testing…

Boards are in the house!

IMG_9608

JLCPCB did a nice job, and the boards look fine. Except:

IMG_9609

Yeah. Those pins are beautifully aligned a very precise 0.1” from where they are supposed to be…

Pro tip: Print out your design and put your components on it so that you can check the design.

Meta pro tip: Follow your pro tips.

Anyway, that’s not the only problem; it turns out that the power and LED parts of the connector are right underneath the end of the board, so you can’t use a normal header on them (you could use a right-angle one if you wanted), so I did a new revision of the board with 1.0” rather than 1.1” for the ESP and extended the board so the connectors are out on the end. That’s on the slow ship from China right now.

Then I did a bit of bodging with some long-tail female headers so I could still do testing.

IMG_9611

Then I put a header for the LEDs and carefully soldered 8 resistors and LEDs to the output pins, so that I have an 8-channel version available for writing software.

IMG_9612

The MOSFETs are pretty darn small, but soldering them was mostly okay. I didn’t bother doing a stencil for this rev so I could reflow, but I will likely do that for the next version.

I have not yet tested what I think is the coolest part of the design; the board is both a main board and an expander board; you can connected a second version of the board on the back of the one with the ESP32 connected to it, and it will get you channels 9-16.

Here’s a quick video of the current state:



Sequence controller test from Eric Gunnerson on Vimeo.

It’s doing a “breathe” on all 8 LEDs with varying timespans for the delay action.

This is the 6th or 7th time that I’ve written sequencing software; there was a Motorola HC11 version, two AVR versions with AC dimming, a 4-channel chaser, and a couple of WS2812 versions.

They were all very simple; take the current state of all the output and drive the outputs to a new state over a given period of time. That works fine, but writing the animation can be annoying and it’s not very compact. This time I wanted to do something different and more elegant:

Here’s my spec:

     IMG_9614

That means ‘loop variable %A from 0 to 7’, and then execute a 100 cycle (1 second) dim of channel %A from its current state to 1.0 (full bright), and then do the same dim back down to zero.

I also wanted to write the vast majority of the code on my desktop, so I took a break and wrote three blog posts about how I do that. It’s basically compile-time dependency replacement with unit tests mostly written using TDD.

Then it was off to writing a *lot* of test code and a lot of classes; 18 difference source files, only two of which are ESP specific at this point. And 15 test classes to drive the tests. It mostly worked great, I did 95% of my coding and only had once latent bug that I had to track down on the ESP32. It was weird one that turned out to have very random behavior. I suspected it was uninitialized data, and that turned out to be mostly right; two subsequent calls to a method used the same stack and I forgot that strncpy doesn’t copy a null. But it all works now. Here’s the code the video is running:

$1$LOOP %B 100:10:-10
    $1$LOOP %A 0:7
        $%B$D%A,1.0
        $%B$D%A,0.0
    $1$ENDLOOP
$1$ENDLOOP”

Variable %B is used to change the cycle count for the operations from 100 to 10 in steps of 10, and then the inner loop cycles through the 8 different outputs. Everything works great.

The code all lives here if you want to see the actual code or a more realistic testing example.

Next steps:

  1. Wireless implementation to do the connection to the ESP
  2. Save and load of the animation
  3. Web interface to edit the animation.
  4. Change the language to move the cycle count into the “dim” command, as it’s not necessary for the loop commands.
  5. Build a second board to test channels 9-16.





Write and debug your Arduino programs on your desktop part 3: Fading

In the first post I said we would be doing a Larson scanner, and all we’ve done so far is make a light that goes back and forth in different colors. That is cool and all, but what about the FADING!

In standard Larson scanner, this is pretty easily done; you just need a way to go backwards for <n> steps and then you just write the bright color at the current spot and then dim it as you go backwards. With the color wheel, however, you would need to keep track of what the colors of the previous spots were and dim that color.

Seems like a lot of work to me.

Instead, we’re going to be building something that I think is a little cooler – a fading LED strip. Set a point to a specific color, and over <N> steps, it will automatically fade to black.

Hmm. It sounds like what we need is some code that can blend from a color to black. We already have a class that can do that – the ColorBlend class. We can just leverage that to do what we want. Here’s a test:

static void TestSingleFade()
{
     FadingLedStrip fadingLedStrip(4);
     LedStrip ledStrip;


    LedColor ledColor;


    fadingLedStrip.setColor(1, 255, 0, 0);


    fadingLedStrip.show(ledStrip);
     ledColor = ledStrip.getColor(1);
     Assert::AreEqual(255, ledColor.Red);


    fadingLedStrip.show(ledStrip);
     ledColor = ledStrip.getColor(1);
     Assert::AreEqual(191, ledColor.Red);


    fadingLedStrip.show(ledStrip);
     ledColor = ledStrip.getColor(1);
     Assert::AreEqual(127, ledColor.Red);


    fadingLedStrip.show(ledStrip);
     ledColor = ledStrip.getColor(1);
     Assert::AreEqual(63, ledColor.Red);


    fadingLedStrip.show(ledStrip);
     ledColor = ledStrip.getColor(1);
     Assert::AreEqual(0, ledColor.Red);
}

We set a color and then each time we call show(), it dims the color down. In this case, the dim count is set to 4, so it will take 4 more steps to dim all the way down.

The code for FadingLedStrip is here:

class FadingLedStrip
{
     int _steps;


    ColorBlender _blenders[15];


public:
     FadingLedStrip(int steps)
     {
         _steps = steps;
     }


    void setColor(int ledNumber, int red, int green, int blue)
     {
         _blenders[ledNumber].blendToColor(LedColor(red, green, blue), 0);
         _blenders[ledNumber].blendToColor(LedColor(0, 0, 0), _steps);
     }


    void show(LedStrip& ledStrip)
     {
         for (int i = 0; i < 15; i++)
         {
             LedColor ledColor = _blenders[i].getCurrentColor();
             ledStrip.setColor(i, ledColor.Red, ledColor.Green, ledColor.Blue);
             _blenders[i].step();
         }


        ledStrip.show();
     }
};

It keeps an array of ColorBlenders – one per LED. When we set a color, we tell the blender to immediately switch to that color, and we also tell it to blend to black of the specified number of steps.

The show() method then walks through all of the blenders and copies the color of each blender to the real strip and tells the blender to step.

Here’s a video of the final result:


Larson Scanner from Eric Gunnerson on Vimeo.


Write and debug your Arduino programs on your desktop part 2: Automated Testing

Read the previous post before you read this one.

In the previous post, I showed how to use hand-verification – and perhaps a debugger – to get your code working. That works well in many cases, but sometimes you have code that you think is going to evolve over time or code where it is tedious to do the hand verification.

The alternate is to automate that verification, using what is commonly known as “Unit Tests”.

Blending colors

The current implementation only uses red, green, and blue. It would be much nicer if it could smoothly change between colors. I’m going to be building a way to blend from the current color to a new color in a specified number of steps.

I’m going to do this implementation in small steps, using a technique where I write the test before I write the code. To start, we need to switch to a new color immediately when the user chooses zero steps.

Here’s my test code:

static void TestZeroSteps()
{
     ColorBlender colorBlender;


    colorBlender.blendToColor(LedColor(255, 0, 255), 0);


    LedColor color = colorBlender.getCurrentColor();
     Assert::AreEqual(255, color.Red);
     Assert::AreEqual(  0, color.Green);
     Assert::AreEqual(255, color.Blue);
}

The test blends to (255, 0, 255) – purple – in zero steps, so the next time getCurrentColor() is called, it should return that color.

The Assert::AreEqual() statements are verifying that the values we get back are the ones we expect; if they are not, a message will be written out to the console.

This test code lives in the ColorBlenderTest.h file.

The code for ColorBlender lives in the arduino project, and looks like this:

class ColorBlender
{
     LedColor _targetColor;


    public:


    LedColor getCurrentColor()
     {
         return LedColor(_targetColor.Red, _targetColor.Green, _targetColor.Blue);
     }


    void blendToColor(LedColor targetColor, int steps)
     {
         _targetColor = targetColor;
     }
};

When run, that produces no errors. In the next test, we’ll do the blend in one step. Here’s a new test:

static void TestOneStep()
{
     ColorBlender colorBlender;


    colorBlender.blendToColor(LedColor(255, 0, 255), 1);


    LedColor color = colorBlender.getCurrentColor();
     Assert::AreEqual(0, color.Red);
     Assert::AreEqual(0, color.Green);
     Assert::AreEqual(0, color.Blue);


    colorBlender.step();


    color = colorBlender.getCurrentColor();
     Assert::AreEqual(255, color.Red);
     Assert::AreEqual(0, color.Green);
     Assert::AreEqual(255, color.Blue);
}

The initial color should be black, and then after calling step(), it should move to the new color. When this is run, we get the following:

Assert: expected 0 got 255
Assert: expected 0 got 255

We get those errors because there is no implementation to make the test work. This code will make it work:

class ColorBlender
{
     LedColor _currentColor;
     LedColor _targetColor;


    public:


    LedColor getCurrentColor()
     {
         return LedColor(_currentColor.Red, _currentColor.Green, _currentColor.Blue);
     }


    void blendToColor(LedColor targetColor, int steps)
     {
         _targetColor = targetColor;


        if (steps == 0)
         {
             _currentColor = _targetColor;
         }
     }


    void step()
     {
         _currentColor = _targetColor;
     }
};

and now, onto two steps. Here’s the test:

static void TestTwoSteps()
{
     ColorBlender colorBlender;


    colorBlender.blendToColor(LedColor(255, 0, 255), 2);


    LedColor color = colorBlender.getCurrentColor();
     Assert::AreEqual(0, color.Red);
     Assert::AreEqual(0, color.Green);
     Assert::AreEqual(0, color.Blue);


    colorBlender.step();


    color = colorBlender.getCurrentColor();
     Assert::AreEqual(127, color.Red);
     Assert::AreEqual(0, color.Green);
     Assert::AreEqual(127, color.Blue);


    colorBlender.step();


    color = colorBlender.getCurrentColor();
     Assert::AreEqual(255, color.Red);
     Assert::AreEqual(0, color.Green);
     Assert::AreEqual(255, color.Blue);
}

and the updated code:

class ColorBlender
{
     float _red = 0.0F;
     float _green = 0.0F;
     float _blue = 0.0F;
     float _redDelta = 0.0F;
     float _greenDelta = 0.0F;
     float _blueDelta = 0.0F;


    LedColor _targetColor;


    public:


    LedColor getCurrentColor()
     {
         return LedColor((int) _red, (int)_green, (int)_blue);
     }


    void blendToColor(LedColor targetColor, int steps)
     {
         _targetColor = targetColor;


        if (steps == 0)
         {
             _red = _targetColor.Red;
             _green = _targetColor.Green;
             _blue = _targetColor.Blue;
         }
         else
         {
             _redDelta = (_targetColor.Red – _red) / steps;
             _greenDelta = (_targetColor.Green – _green) / steps;
             _blueDelta = (_targetColor.Blue – _blue) / steps;
         }
     }


    void step()
     {
         _red = _red + _redDelta;
         _green = _green + _greenDelta;
         _blue = _blue + _blueDelta;
     }
};

That works. One more test to add; we should stop blending even if we go beyond the specified number of steps. Here’s a test for it:

static void TestThreeStepsAndHold()
{
     ColorBlender colorBlender;


    colorBlender.blendToColor(LedColor(10, 0, 0), 3);


    Assert::AreEqual(0, colorBlender.getCurrentColor().Red);
     colorBlender.step();


    Assert::AreEqual(3, colorBlender.getCurrentColor().Red);
     colorBlender.step();


    Assert::AreEqual(6, colorBlender.getCurrentColor().Red);
     colorBlender.step();


    Assert::AreEqual(10, colorBlender.getCurrentColor().Red);
     colorBlender.step();


    Assert::AreEqual(10, colorBlender.getCurrentColor().Red);
}

That fails on the last assert, as it keeps adding and gives us 13. I added some code and ended up with this:

class ColorBlender
{
     float _red = 0.0F;
     float _green = 0.0F;
     float _blue = 0.0F;
     float _redDelta = 0.0F;
     float _greenDelta = 0.0F;
     float _blueDelta = 0.0F;


    LedColor _targetColor;
     int _steps;


    public:


    LedColor getCurrentColor()
     {
         return LedColor((int) _red, (int)_green, (int)_blue);
     }


    void blendToColor(LedColor targetColor, int steps)
     {
         _steps = steps;
         _targetColor = targetColor;


        if (steps == 0)
         {
             _red = _targetColor.Red;
             _green = _targetColor.Green;
             _blue = _targetColor.Blue;
         }
         else
         {
             _redDelta = (_targetColor.Red – _red) / steps;
             _greenDelta = (_targetColor.Green – _green) / steps;
             _blueDelta = (_targetColor.Blue – _blue) / steps;
         }
     }


    void step()
     {
         if (_steps != 0)
         {
             _red = _red + _redDelta;
             _green = _green + _greenDelta;
             _blue = _blue + _blueDelta;
             _steps–;
         }
     }
};

All of that was written without and tested without any interaction with my microcontroller.

Doing something nice with the blender…

The blender by itself isn’t that useful; we need something to drive it through different colors. Here’s ColorWheel.h:

class ColorWheel
{
     int _stepCount;
     ColorBlender _colorBlender;
     LedColor _colors[6] = {
         LedColor(255, 0, 0),
         LedColor(255, 255, 0),
         LedColor(0, 255, 0),
         LedColor(0, 255, 255),
         LedColor(0, 0, 255),
         LedColor(255, 0, 255) };
     int _colorIndex = 0;


    public:
         ColorWheel(int stepCount)
         {
             _stepCount = stepCount;
             _colorBlender.blendToColor(_colors[_colorIndex], 1);
         }


        LedColor getNextColor()
         {
             _colorBlender.step();
             LedColor ledColor = _colorBlender.getCurrentColor();
             if (_colorBlender.isDone())
             {
                 _colorIndex = (_colorIndex + 1) % 6;
                 _colorBlender.blendToColor(_colors[_colorIndex], _stepCount);
             }


            return ledColor;
         }
};

It uses a ColorBlender, and whenever a color blender is done – which is checked through a new “isDone()” method – it will add a blend to the next color in the sequence. So it continuously cycles through the 6 main colors (Red, yellow, green, cyan, blue, purple).

It has tests:

#pragma once
#include “..\Arduino\Larson\src\ColorWheel.h”


class ColorWheelTest
{
     static void TestSingleStepWheel()
     {
         ColorWheel colorWheel(1);
        
         LedColor ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(  0, ledColor.Green);
         Assert::AreEqual(  0, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(255, ledColor.Green);
         Assert::AreEqual(  0, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(  0, ledColor.Red);
         Assert::AreEqual(255, ledColor.Green);
         Assert::AreEqual(  0, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(  0, ledColor.Red);
         Assert::AreEqual(255, ledColor.Green);
         Assert::AreEqual(255, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(  0, ledColor.Red);
         Assert::AreEqual(  0, ledColor.Green);
         Assert::AreEqual(255, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(  0, ledColor.Green);
         Assert::AreEqual(255, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(  0, ledColor.Green);
         Assert::AreEqual(  0, ledColor.Blue);
     }


    static void TestFourStepWheel()
     {
         ColorWheel colorWheel(4);


        LedColor ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(0, ledColor.Green);
         Assert::AreEqual(0, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(63, ledColor.Green);
         Assert::AreEqual(0, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(127, ledColor.Green);
         Assert::AreEqual(0, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(191, ledColor.Green);
         Assert::AreEqual(0, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(255, ledColor.Red);
         Assert::AreEqual(255, ledColor.Green);
         Assert::AreEqual(0, ledColor.Blue);


        ledColor = colorWheel.getNextColor();
         Assert::AreEqual(191, ledColor.Red);
         Assert::AreEqual(255, ledColor.Green);
         Assert::AreEqual(0, ledColor.Blue);
     }


public:
     static void RunTests()
     {
         TestSingleStepWheel();
         TestFourStepWheel();
     }
};

and that makes the Animater simpler. Here’s the running code:

#define NUM_LEDS 15


class Animater
{
     int _last = 0;
     int _current = 0;
     int _increment = 1;


    int _color = 0;
     ColorWheel _colorWheel;


    public:


    Animater() : _colorWheel(20) {}


    void doAnimationStep(LedStrip &ledStrip)
     {
         ledStrip.setColor(_last, 0, 0, 0);
         LedColor ledColor = _colorWheel.getNextColor();


        ledStrip.setColor(_current, ledColor.Red, ledColor.Green, ledColor.Blue);
         ledStrip.show();


        _last = _current;
         _current = _current + _increment;


        if (_current == 0 || _current == NUM_LEDS – 1)
         {
             _increment = -_increment;
         }
     }
};









Write and debug your Arduino programs on your desktop

Working on projects with an Arduino – or with other microcontrollers – can be a lot of fun. It can also be a frustrating experience; you write some code and then you need to wait for it to be compiled, packaged up, downloaded to your microcontroller, and then run. And when it doesn’t work, it can be difficult to figure out why it isn’t working; generally, the best you can do look at the output from Serial.println() or look at the signals on an oscilloscope, if you own one.

As a (now former) professional developer, I was sure that there was a better way, and after some experimentation I came up with the method described in these posts.

Basically, the approach is pretty simple. We are going to structure our software so that there are two discrete parts; a first part that is directly dependent on the microprocessor and libraries and a second part that is generic. The second part will contain the bulk of the code that we will be writing.

And then, we are going to use a generic C++ environment to run the code from the second part and verify that it works. Once we have it working in that environment, we can then move over to the arduino environment and test it on the real hardware.

In addition to making it easier to write and verify code, this will also break our code into parts and make it simpler to understand.

Tools

We will be using two different development environments. For our Arduino code, we need an Arduino environment – what I would call an “IDE”. I’m going to be using Visual Studio Code with the Platform IO package, but you can use the Arduino IDE if you’d rather. Both of those are free.

We will also need a desktop/laptop environment that can run C++ code. There are several good options here; I’m going to stay true to my roots and use Visual Studio Community with C++ support installed (also free) for that development, but you can use whatever environment you would like.

The project

A quick look at my blog will indicate that I am quite devoted to LEDs, so that’s what we’re going to build. Specifically a Larson Scanner:

Or at least something like that; I’m not sure we’re going to get to the dimming trail part. If you want to follow along, you’ll need a microcontroller that can run the FastLed library (I’m going to use an ESP8266 board), and a strip of WS2812/Neopixel LEDs. There’s a nice intro to using FastLED in their wiki here, and I suggest getting something working in your environment using their directions before trying to follow along.

I’ll add a note here that if you are using the ESP8266, Makuna’s NeoPixelBus is a better choice than FastLed as it has hardware support, but FastLed is more popular so that’s why I’m using it here.

First Version

All of the code lives in the LarsonScanner repository. I will try to keep my commits small and informative so you can see what the changes are.

I started by writing a quick and minimal version of the code. It looks like this:

#include <Arduino.h>
#define FASTLED_ESP8266_NODEMCU_PIN_ORDER
#include <fastled.h>


#define NUM_LEDS 15
#define DATA_PIN 3


CRGB leds[NUM_LEDS];


void setup() {
   FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
}


int last = 0;
int current = 0;
int increment = 1;


void loop() {
  leds[last] = CRGB::Black;


  leds[current] = CRGB::Red;
   FastLED.show();
   last = current;
   current = current + increment;


  if (current == 0 || current == NUM_LEDS – 1)
   {
     increment = -increment;
   }


  delay(30);
}

Most of this is boilerplate FastLED code. The code itself is simple; it has three variables:

  • current defines the next LED that we need to turn on
  • last defines the LED that is currently on that we will need to turn off
  • increment defines the direction we are moving with the animation

The loop code sets the previous LED off and turns the new one on, and then increments to move to the next LED. If that puts us at the ends of the strip – either LED 0 or LED NUM_LEDS-1 – then that tells us we need to start going the other direction and we negate increment to do that.

Running this code on the desktop

Our goal is to be able to run the code that we wrote – the code to do the animation – on the desktop. But we can’t do this because that code calls the FastLED library, and there’s no FastLED library on the desktop. What we will need to do is provide a way for our animation code to *use* the FastLED code indirectly rather than referring to it directly.

In software development terms, we’re doing what is called “encapsulation”; taking all of the code related to a specific operation and separating it from the rest of our code.

We will take all the FastLED code and move it into a separate class. It looks like this:

#define FASTLED_ESP8266_NODEMCU_PIN_ORDER
#include <fastled.h>


#define NUM_LEDS 15
#define DATA_PIN 3


class LedStrip
{
     CRGB _leds[NUM_LEDS];


    public:
     void setup()
    {
         FastLED.addLeds<NEOPIXEL, DATA_PIN>(_leds, NUM_LEDS);
     }


    void setColor(int ledIndex, int red, int green, int blue)
     {
         _leds[ledIndex] = CRGB(red, green, blue);
     }


    void show()
     {
         FastLED.show();
     }
};

This new class now keeps track of the details of dealing with FastLED. Note that the “leds” array has been renamed “_leds”; that is a naming convention to make it easier to know that it belongs to this class.

Our main code now looks like this:

#include <Arduino.h>
#include <LedStrip.h>


LedStrip ledStrip;


void setup() {
   ledStrip.setup();
}


int last = 0;
int current = 0;
int increment = 1;


void loop() {


  ledStrip.setColor(last, 0, 0, 0);
   ledStrip.setColor(current, 255, 0, 0);
   ledStrip.show();


  last = current;
   current = current + increment;


  if (current == 0 || current == NUM_LEDS – 1)
   {
     increment = -increment;
   }


  delay(30);
}

Better. There are no FastLED details in here, but there are still arduino details that would get in the way of using it from the desktop. Just as we took all the FastLED details and put them in a class, we will now put all of the animation details into a separate class. It looks like this:

#define NUM_LEDS 15


class Animater
{
     int _last = 0;
     int _current = 0;
     int _increment = 1;


    public:


    void doAnimationStep(LedStrip &ledStrip)
     {
         ledStrip.setColor(_last, 0, 0, 0);
         ledStrip.setColor(_current, 255, 0, 0);
         ledStrip.show();


        _last = _current;
         _current = _current + _increment;


        if (_current == 0 || _current == NUM_LEDS – 1)
         {
             _increment = -_increment;
         }
     }
};

That puts all the code in a method named doAnimationStep; we pass in an LedStrip, and it does whatever it needs to do.

Our main program code gets even simpler:

#include <Arduino.h>
#include <LedStrip.h>
#include <Animater.h>


LedStrip ledStrip;
Animater animater;


void setup() {
   ledStrip.setup();
}


void loop() {
   animater.doAnimationStep(ledStrip);
   delay(30);
}

This demonstrates quite well why encapsulation is a good thing; instead of having one main program with different things going on, we have three sections of code; the code that only deals with FastLED operations, the code that deals with the animation, and then a very simple bit of code that hooks them together. If your arduino code is getting complicated and hard to understand, using encapsulation will help immensely.

The desktop version

We are now ready to run our animation code. I’ve created a Visual Studio C++ project named “ConsoleTest” next to the arduino project. My goal is to run the code in Animater.h in this environment, and to do that, I’m going to need a different implementation of LedStrip.h. Here’s what I create in the ConsoleTest project:

class LedStrip
{
public:
     void setColor(int ledNumber, int red, int green, int blue)
     {
         printf(“LED %d: (%d, %d, %d) \n”, ledNumber, red, green, blue);
     }


    void show()
     {
         printf(“Show: \n”);
     }
};

Instead of talking to an LEDStrip, it just writes out the information it is called with to the console.

The ConsoleTest.cpp file in this project looks like this:

#include “stdafx.h”
#include “LedStrip.h”
#include “..\Arduino\Larson\src\Animater.h”


int main()
{
     LedStrip ledStrip;
     Animater animater;


    for (int i = 0; i < 30; i++)
     {
         animater.doAnimationStep(ledStrip);
     }


    return 0;
}

It includes the printing version of LedStrip in the test project, but it then includes Animater.h from the arduino project. The main() function then calls the animation code the same way the arduino code would call it. It generates the following output:

LED 0: (0, 0, 0)
LED 0: (255, 0, 0)
Show:
LED 0: (0, 0, 0)
LED 1: (255, 0, 0)
Show:
LED 1: (0, 0, 0)
LED 2: (255, 0, 0)
Show:
LED 2: (0, 0, 0)
LED 3: (255, 0, 0)
Show:
LED 3: (0, 0, 0)
LED 4: (255, 0, 0)
Show:
LED 4: (0, 0, 0)
LED 5: (255, 0, 0)
Show:
LED 5: (0, 0, 0)
LED 6: (255, 0, 0)
Show:
LED 6: (0, 0, 0)
LED 7: (255, 0, 0)
Show:
LED 7: (0, 0, 0)
LED 8: (255, 0, 0)
Show:
LED 8: (0, 0, 0)
LED 9: (255, 0, 0)
Show:

We are now able to see the calls the animation code would make to the FastLED library when it runs on the arduino and see if it is behaving as expected.

Digression for experienced developers

If you aren’t an experienced developer, you can safely ignore this section.

This technique – which I call “abstraction by include file” – likely looks a little weird. The “right” way to do this in C++ is to define a pure abstract class named ILedStrip with pure virtual functions that are then overwridden by LedStrip in the arduino code and by a LedStripTest class in the console project.

I’ve implemented this technique both my way and the “right” way, and I’ve found that the right way requires an extra interface definition and doesn’t really help the resulting code. And it requires understanding virtual methods. But that’s an aesthetic choice; feel free to make the opposite choice.

Modifying our animation…

Let’s say that we now want our animation to change colors each time it switches direction. Can we write that code and test it without downloading it to the Arduino?

Here’s my crappy implementation:

#define NUM_LEDS 15


class Animater
{
     int _last = 0;
     int _current = 0;
     int _increment = 1;


    int _color = 0;


    public:


    void doAnimationStep(LedStrip &ledStrip)
     {
         ledStrip.setColor(_last, 0, 0, 0);
         if (_color == 0)
         {
             ledStrip.setColor(_current, 255, 0, 0);
         }
         else if (_color == 1)
         {
             ledStrip.setColor(_current, 0, 255, 0);
         }
         else
         {
             ledStrip.setColor(_current, 0, 0, 255);
         }
         ledStrip.show();


        _last = _current;
         _current = _current + _increment;


        if (_current == 0 || _current == NUM_LEDS – 1)
         {
             _increment = -_increment;


            _color = _color + 1;
             if (_color == 3)
             {
                 _color = 0;
             }
         }
     }
};

Basically, it has a color variable that increments each time we switch directions, and we check that variable to decide what color to set.

By examining the output, we can see if the program is doing what we expect. Or we can use the debugger that is built into Visual Studio Community to have the program stop at any line in our code so that we can see what the values of variables are and what code is being executed. That is much much easier than trying to figure out what is going on in code running on the arduino. There’s a nice introduction to using the debugger here.

Tracking state

To verify an animation, we have to read a lot of output and keep track of which LEDs are which colors. We can make that a little easier by modifying our test LedStrip class so that it keeps track for us. First, we’ll need class that can hold the state of one LED:

class LedColor
{
public:
     int Red;
     int Green;
     int Blue;


    LedColor() : LedColor(0, 0, 0)
     {
     }


    LedColor(int red, int green, int blue)
     {
         Red = red;
         Green = green;
         Blue = blue;
     }
};

And then we can use that class in our LedStrip class:

class LedStrip
{
     LedColor _colors[15];


public:
     void setColor(int ledNumber, int red, int green, int blue)
     {
         _colors[ledNumber] = LedColor(red, green, blue);
     }


    void show()
     {
         printf(“Show: “);
         for (int i = 0; i < 15; i++)
         {
             printf(“(%d,%d,%d)”, _colors[i].Red, _colors[i].Green, _colors[i].Blue);
         }
         puts(“”);
     }
};

This generates the following output:

Show: (255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)
Show: (0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(0,0,0)(255,0,0)(0,0,0)(0,0,0)(0,0,0)

I find this approach to be a bit more visual and easier to understand.

That’s a good place to stop for this post. The next post will explore automated testing using this approach.

Part 2: Automated testing


Sequence Controller

I’m working on a new display for the upcoming holiday season – actually a couple of them – and I need some new controller hardware to drive them.

Here are the basic requirements that I jotted down:

  • 8 outputs (perhaps expandable to 16)
  • Each output can drive 1 amp at 12V
  • Designed to deal with sequential animation (do this on output 1, do something else on output 2, etc.)
  • Dimming support if practical
  • Easy setup and and configuration
  • Compact & cheap (within reason)…

With those in mind, I started thinking about components…

My go-to microcontroller has been the ESP8266 (in NodeMcu mini d1 form) for a while, because it’s so small and cheap. But it’s a bit weak on output pins; you can get 7 pretty easily, but to get more you may have to play tricks. Supposedly you can get to 11 with those tricks which would be okay for 8 but would make 16 possible without some sort of I/O expander.

Which brings me obviously to the ESP32. Which is honestly a ridiculously capable device; 160 Mhz, 520K of SRAM, dual core (if you need it), Wifi, bluetooth, and pretty much all the I/O support you could want. It’s a little more pricey, about $4 from China in single quantities.

For this project, it has loads of output pins, and 16 independent PWM channels, which fits pretty well into my requirements. And I’m hoping I can adapt my existing controller software – which is optimized to drive WS2812s – to work in this new scenario.

MOSFETs

The switching will of course be handled by n-channel MOSFETS. My WS2812 expander uses DPAK (TO-252/TO-263) packages, which work great but take up a lot of real estate. That was okay for a small number of channels, but for 8 channels I’d like something smaller and I don’t need to be driving 10 amps per channel, which was my design goal for the expander.

So, my requirements are:

  • 1 amp @ 12V
  • Switchable from 3.3V outputs (I could add a transistor to drive, but I’d rather avoid the complexity)
  • Low Rds at 3.3V
  • Small package
  • Enough power dissipation

I started doing some parametric searches in DigiKey and on Octopart, narrowed things down, and came across the BSR202N from Infineon. How does it stack up?

  • 3.8 amps @ 12V (25 degrees, 3.1 at 70 degrees)
  • Specified behavior down to 2.5V.
  • Rds of 33 milliohms at 2.5 V.
  • SOT23 compatible package
  • 500 mW power dissipation

Those specs are honestly ridiculously good, especially the Rds. If I pull 3 amps through one of the channels, that gives me 0.033 ohms * 3 = 0.1 watts. Just a tenth of a watt to switch 3 amps. If I did that with a bipolar, it would be in the range of 1.8 watts (I’d definitely need a heatsink) and I’d lose 0.6 volts in the process.

In reality, it will likely be a little better than that since the Rds is lower at 3.3V, but I don’t know how much 3 amps will be heating it up and that will make the Rds worse. It will take some testing to see.

My only big concern whether the ESP32 has enough drive to deal with the gate capacitance while doing PWM, as with PWM it’s switching all the time, and slow transitions mean slower switching, more heating, and therefore worse Rds and more heating. I’ll need to do some testing, but my guess is that with a PWM rate of 250 Hz it probably won’t be a significant problem in typical usage patterns.

If it does turn out to be an issue, I’ll add a small bipolar in front of the mosfet. That will give me lots of drive for very fast switching plus a higher Vgs for a lower Rds. It will invert the PWM so I’d have to flip things in software, but that’s simple enough. I’m hoping to avoid it because it will require two resistors per channel, so it’s a nice 8 MOSFETs by themselves or with an added 8 bipolars and 16 resistors, which makes building the boards more of a pain (the cost is of the bipolars + resistors is a few cents per channel).

Expandability

My current thought is to make the boards stackable like arduino shields, and I think I have a scheme that works.

I have the ESP32 boards in my hot hands, but I need to get my hands on some of the MOSFETS to do testing. In parallel, I’m going to start the board design.



Fixing Bally/Williams Opto troughs

I had a problem with my WCS serving multiple balls, and I thought I’d share the approach I used to fix it.

I had looked at the switch tests, but the problem was somewhat intermittent so that didn’t really help.

I pulled the whole trough unit out; that took:

  • Two screws from the bottom
  • Removing the bottom playfield cover below the flippers
  • Removing 5 (?) screws from the top

That loosens the trough. I then took out the 4 screws that hold the solenoid to the trough, unplugged the connectors, and it was out.

Test the LEDs

I started by testing the LEDs. Through trial and error, I found that a 1K ohm resistor and a 5V supply resulted in a current of about 4mA, and since that’s within the spec for most LEDs, I stuck with that. Hook one end of the supply to the common and the other to the individual LED pins, and verify that they all light up.

They’re infrared LEDs, so you can’t see them, but pretty much any digital sensor can; a camera, your phone, etc. It’s simpler to remove the board from the trough before you do this.

All the LEDs on my board checked out.

Test the phototransistors

Keep your setup to turn on the LEDs as we’ll need it for this step.

Using a ohmmeter, connect one end to the common and then connect the other end to the pin for the LED that you currently have on. You should see about 4K ohm when the LED is on and something around 1M ohm when your hand is blocking the light. If you don’t see any difference, swap the leads from the ohmmeter around. You may have to turn the lights out to get 1M on some of the phototransistors as you can get room light reflecting into them.

Work your way down through each LED and phototransistor and verify that you are getting the right settings. If you find one that isn’t reading correctly, or consistently, it is *most likely* a connection issue.

I would start by verifying the connections; with one lead connected to the common pin, verify that you have continuity to all of the phototransistors; one of the pins on every one should be zero ohms (or close to it) and connected to the common.

Then repeat that from each of the LED pins on the connector to the non-common phototransistor pin. You should see zero ohms on each of those as well.

My issue turned out to be a rework issue; the #6 phototransistor was replaced by somebody and they either messed up the through-hole or didn’t resolder it correctly, so it was only making contact on the LED side of the board sporadically. Rather than pull the board off and try to resolder the phototransistor, I added a small jumper wire from the pin to the phototransistor.

Everything tested fine, and no more double balls.


Pogo pins + laser cutter = test fixture

I sell a small WS2811 expander board on Tindie:

WS2811 / WS2812 Extender 1

It’s not a particularly complex board, but it still needs to be tested, and at minimum that test requires 9 connections to the board. For the prototypes I just soldered wires on, but for the ones I sell that would not be a good idea, and it would also be a fair bit of extra work.

I decided to build a pogo-pin test jig, and since the approach I came up with was different than the other approaches I’ve seen I thought it would be worth sharing. I’m going to be targeting my laser cutter for fabrication, though I could have chosen to use my 3D printer instead.

Pin design and layout

I’m going to be using Kicad for my examples here; if you use something different, you’ll need to figure out how to do some operations ourselves.

Here’s the starting design:

image

For testing, I need to provide connections to a subset of the all of the headers. I’m going to do the design for all of the headers and then just populate the ones that I need. For many boards, you would have test pads that are unpopulated as the targets for your testing.

I need a way to get this into a format I can use with my laser cutter, and SVG is the one I’d like. Kicad can export to SVG just fine; you use the Gerber export and choose “SVG” as the format (no, it doesn’t really make a lot of sense). I’ll be using the pins to connect to the copper, so I’m going after the copper layer.

Once it’s exported, I can open it up in Inkscape for editing:

image

I’m going to clean this up to get rid of all the parts that I don’t need. In some cases, the components are grouped and I need to ungroup them.

image

What I want to do is put a pogo pin at the middle of each of these. These are the pogo pins that I’m using:

image

They are spec’d to have 1mm shafts and they’re quite close to that, so we’ll plan our design using that.

At this point, we need to account for one of the things that Inkscape does. The UI allows you to set the size of a circle, but the size that you set is the outer side of the circle, and that includes the stroke width. If your stroke width is 0.25mm and you set the circle to 1mm, the actual circle will only be 0.5mm.

This confused me for a while when I did my first prototype. And then it confused me again when I did this version. The fix is to set the stroke width to 0 – or something very close to 0 – and use a filled circle instead.

Here’s a picture of a pad and a 1mm red circle I want to center inside of it:

image

I need to get that red circle centered inside the pad. Because of the way Inkscape works, you need to start with the small circle on the lower left. I don’t know why. Then do the following operations:

  • Drag select both objects.
  • Choose “align top”
  • Choose “align right”
  • That should move only the red circle.
  • Align center on the horizontal axis
  • Align center on the vertical axis.

To select the circles I need to drag a region; it doesn’t work trying to select the object. I don’t know why.

That gives us the following:

image

Eventually, I will want one of those for every pin. But I need to do some test sizing first.

The beam on a laser cutter is pretty thin, but it still has some width. That means if I cut out a 1.0mm circle, I’ll get one that is just a bit bigger than that, and the pins will be loose.

I’m going to use the row of 6 pads at the top for test sizing.

image

You can’t tell from the picture, but these are sized 1.0 mm, 0.975 mm, 0.950 mm, 0.925 mm, 0.900 mm, and 0.875 mm.

Off to the laser cutter!

IMG_9553

The test fixture cut out of 0.1” (2.6mm) maple plywood.

The 0.875mm hole is the only one that is close; it’s a tiny bit snug in the middle (the laser beam is shaped like a very tall “X”, so the hole is thinnest at the focus point in the middle and a little bigger at the top and bottom).

Based on the step in sizing between holes, I’m going to size them all to 0.85 mm.

image

That’s the completed design. In my laser cutter (a GlowForge), it groups the elements by color, so it’s easy for me to tell it to cut the red circles and not the black pads. If you want to simplify the cutting part, you might want to delete the pads.

Back to the cutter.

IMG_9554

I cut 3 identical plates plus a spacer. The stack will have two plates with holes, then the spacer level where the wires will be soldered to the pins and finally a plate at back. The wires will all come out through the spacer holes on the left. It’s hard to tell from the picture, but the pins are inserted so that they stick out the back enough for the bottom of the pin to be flush when the spacer and bottom plate are added.

Oh, and that lower-left piece is upside down…

Next is hooking up the test wires. There are 22 pin holes, but two of them need only to be bridged and seven connected with wires for testing.

IMG_9560

That’s a rather poor picture of the wires attached to the pogo pins. After they are all soldered on, the back piece goes on and then I taped it together with blue tape so the pieces are apart.

That would normally be the last step, but the high-current pins on this board are set up using 3.96mm headers instead of the 2.54mm that my pogo pins are designed for, so those pins just go right through the bulbs. With a little bit of play using a soldering iron, you can get a blob of solder on each of those pins and then it will work fine.

Here’s the completed tester with a board just sitting on it.

IMG_9562

The yellow and green wires come from an ESP8266 that I use to drive it, the red and black are 12V power from a repurposed XBox 360 power supply, and then the white wires are the grounds for the loads (the board provides both positive and ground for each LED, but I only need the grounds to do the testing).

I made a quick video showing the tester in action.


MVI_0093 from Eric Gunnerson on Vimeo.


Pages:1234567