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.





Just enough biochemistry – Insulin resistance and Gluconeogenesis

Disclaimer: “Just enough biochemistry” means I’m going to be simplifying what is some very complex biochemistry. Sometimes the omitted details are going to matter.

****

There’s this weird thing that happens as part of insulin resistance that I don’t think gets discussed often enough, and since I think it drives much of what is bad about insulin resistance, I’m going to discuss it here. We’ll start with glucose metabolism:

Elevated blood glucose / elevated insulin

When blood glucose is elevated, the pancreas secretes insulin, and that changes how the body’s metabolism operates:

image

Insulin is a signal to pull excess glucose out of the blood. The body has 4 ways to do this, shown as purple arrows in the diagram. The are, from the right to the left:

  1. Store it as muscle glycogen. There are a lot of muscle cells in the body and the conversion to glycogen is quick, so they can take up a lot of glucose quickly – that is is why the arrow is so big. But their ability to take up glucose is dependent on how much glycogen they already are storing – how much space there is for new glycogen. There be a lot of extra space after a long run and very little space after a few hours watching TV.
  2. Store it as liver glycogen. The liver can’t store quite as much glycogen, but it can still store it pretty quickly.
  3. Burn it as it comes into the bloodstream. The size of this arrow depends on what you are doing; if you are just sitting it’s pretty small, if you are exercising it can be bigger. With high insulin the body will choose to burn more glucose and less fat; notice the small size of the fat arrow.
  4. Convert it to fat. Excess glucose that can’t be handled in the other ways will be converted to fat and stored through a process known as “de novo lipogenesis” (“new fat creation”). The body isn’t very fast at doing this, so this arrow is small. If there is a lot of glucose to convert to fat, this process will take hours.

Broadly speaking, this part of the diagram is about storing glucose energy that is coming in from the diet.

Low blood glucose / low insulin

When blood glucose is low, the pancreas secretes glucagon:

image

When the blood glucose is low, the glucagon is a signal to do something to increase the glucose in the blood.

There are only two sources – outside of eating – to raise blood glucose when it is low:

  1. The glycogen that was stored in the liver previously can be converted back to glucose and released into the bloodstream. This is good for the short term, on the order of a few hours, but at some point the liver will run out of glycogen.
  2. If glycogen stores run low, then the body will convert metabolic leftovers into glucose through a process known as “gluconeogenesis” (new glucose creation). This might be temporary until more carbs are eaten, or it might be continuous if there is little glucose coming through food.

When glucose is rare, the body will try to conserve it, so it will burn proportionally more at; that is why the fat arrow is bigger in this part of the diagram.

There is another process – ketosis – that kicks in when there is little glucose coming in from the diet, but that’s a bit beyond the scope of this discussion.

This part of the picture is about running the body on energy that has been previously stored.

The full picture

Here’s the full picture:

image

This diagram shows how a healthy – insulin sensitive – metabolism works. If you eat a meal that has enough carbs to raise your blood glucose, you will be in the top half of the diagram. Between meals – and especially at night – you will be on the bottom portion of the diagram.


Blood glucose and insulin resistance

One of the tests for insulin resistance/type II diabetes is the Oral Glucose Tolerance Test. In the OGTT a person who has fasted overnight drinks a solution with 50 grams of glucose in it and their glucose level is measured every 30 minutes.

image

This chart shows data from an OGTT for three different people.

Because these people have fasted overnight, we would expect them to have used liver glycogen to keep their blood glucose up for most of the night, and that would leave them with some room to store most of the glucose from the test. If that were true, the glucose could be stored quickly without a lot of blood glucose change and a couple of hours later things would be back to normal. And that is what we see on the “normal chart”.

The discussion about the other lines would typically focus on how much higher the blood glucose levels are and how much longer they take to get back to the starting point, but I want to focus on another factor. Note that the other lines start at significantly elevated blood glucose levels. I find this puzzling; let me explain why…

Looking at the patient with moderate type II diabetes, we see that in a span of 90 minutes, they got rid of a lot of blood glucose, from 280 mg/dl to 170 mg/dl, for a difference of 110 mg/dl. Or something like 70 mg/dl in an hour.

But overnight, they can’t get their blood glucose below 130 mg/dl, despite not eating anything and having 8-12 hours in which it could happen. We would expect that the natural use of glucose would drop the blood glucose to normal levels by morning; in fact, it would drop it too much if the liver didn’t add glucose.

Where is the glucose coming that is keeping that from happening?

That is the puzzle.

Looking back at the diagram, there are only two possible sources for the glucose. Maybe it could come from the glycogen stores, but if that were the case we would expect them to be more depleted in the morning and therefore be able to handle the 50 grams of glucose from the test. That doesn’t appear to be the case.

Which points the finger very strongly at gluconeogenesis.


Gluconeogenesis malfunction

In the diagram, I showed gluconeogenesis on the bottom; it is only active when blood glucose was low and glycogen stores are low.

Unfortunately, when somebody becomes insulin resistance, something gets broken with the regulation of gluconeogenesis. Normally it gets turned off when there is insulin in the system, but that gets broken when there is an accumulation of fat in the liver.

The result is a constant supply of glucose into the bloodstream, with the amount related to how much fat has accumulated in the liver.

What does this mean metabolically?

A constant stream of unexpected glucose means that between meals and overnight, we are depleting our glycogen reserves less. That is the big effect we are seeing in the charts; there is less free room in the glycogen store, so more of the glucose that we eat needs to be converted to fat. That not only means more fat in our fat stores, it takes longer to do this conversion, so our insulin levels are elevated for longer. And any time that our insulin levels are elevated is time when we’re on the upper half of the diagram.. So, more fat stored, less time to burn fat.

Not good. But initially the body is still able to deal with the amount of carbohydrate in the diet and the amount being generated and still keep fasting blood glucose levels normal. It does require more insulin all the time – taking the first step towards hyperinsulinemia – and it will also push up HbA1c levels. That’s why HbA1c is a better diagnostic test for insulin resistance for *most* people than fasting blood glucose. Fasting insulin level is probably better still but is not widely used.

As insulin resistance gets worse, there is more glucose created, enough so the glycogen stores are full most of the time.  Any glucose that can’t be burned off immediately goes straight to fat, and the high insulin percentage – the percentage of time in a day when insulin is elevated – goes up. Hyperinsulinemia gets worse, carbs that are eaten go mostly to fat, and fat eaten goes there as well.

Eventually, the pancreas loses the capacity to produce enough insulin and/or the fat and muscle cells get poor enough at pulling glucose out of the bloodstream, the fasting glucose level goes up, and it’s type II diabetes.

The big point here is that a person who is insulin resistant has different metabolism than one who is not; it’s a bit like they are eating a small amount of candy around the clock.

Diabetes is a chronic disease

For a long time, the ADA recommended high-carb, low fat diets as the only diet for type II diabetes. For people who wanted to lose weight – which is true for most type II diabetics – the recommendation would include a small calorie deficit of around 300-500 calories per day.

What is the impact of that diet for somebody who is insulin resistant and has hyperinsulinemia? We still have the glucose created by the liver and we still have a lot of glucose coming in from the diet, so we’re still going to have the hyperglycemia. As long as the hyperglycemia is there, the insulin is there – we’re on the top half of the diagram – it’s hard to burn fat. So, the body has a few options:

  • Turn down our basal metabolic rate to burn fewer calories.
  • Turn down our optional metabolic activity to burn fewer calories.
  • Convert some protein to energy
  • Try to get us to eat more
  • So, we get cold, tired, hungry, and lose lean muscle mass.

    But we still have the hyperglycemia, insulin resistance, and blood glucose issues.

    This is why type II diabetes is generally considered to be a chronic disease. It’s pretty well known that if people can manage to lose weight, their diabetes symptoms get better, but the prescribed diets are generally ineffective at producing a permanent change. I think that the arrow of causation runs the opposite direction; if you can manage to improve your hyperinsulinemia, you will lose weight.


    Dealing with hyperinsulinemia

    The hyperinsulinemia is at the root of the problem. How can we get the insulin down?

    There’s really only one solution; we need to get the carbohydrates down enough so that the excess glucose produced by the liver is no longer enough to cause significant hyperinsulinemia. If that excess glucose becomes metabolically desirable – necessary to run our bodies – than it will no longer be problematic.

    We need to get back to the bottom part of the diagram. There are a couple of different ways of doing this.

    We could just reduce the amount we eat by a lot. Enough so that the glucose we are eating plus the extra from gluconeogenesis is being put to use to run the body and we don’t need any insulin to store it. We could do this by going on a very low calorie diet, something under 800 calories per day. We could get a gastric bypass, which has a similar effect (gastric bypass also appears to have some additional effect because of the physical changes made). Both gastric bypass and very low calorie diets have good clinical evidence for reversing insulin resistance and getting rid of the symptoms of type II diabetes. Some of the fasting protocols will also likely work, though we don’t have the same quality of studies to support them.

    Or we could just try to reduce the amount of carbs we eat by a lot. So low that we would expect to be relying on gluconeogenesis to produce the glucose that we need and low enough that we don’t have a lot of glucose to process. In other words, a keto or zerocarb diet. And keto diets also have good levels of clinical evidence achieving the result we want.

    With other diets, people get less diabetic and perhaps lose some weight, but still end up diabetic at the end. My assumption is that they don’t do as well because those diets don’t deal with hyperinsulinemia *unless* you convert them to very-low-calorie diets.

    Notes

    As noted, this is a really complex area. For basic biochemistry, there are a number of good references out there; I like “Marks Basic Medical Biochemistry”, which can be found in PDF form online.

    For more of the details, see the following links:

    Insulin resistance and gluconeogenesis

  • Insulin regulation of gluconeogenesis
  • Unraveling the Paradox of Selective Insulin Resistance in the Liver: the Brain–Liver Connection
  • Resolving the Paradox of Hepatic Insulin Resistance
  • Keto diets

  • Virta Health Research Page
  • Virta Spreadsheet of low-carb studies
  • Very-low-calorie diets

  • Primary care-led weight management for remission of type 2 diabetes (DiRECT): an open-label, cluster-randomised trial
  • Very Low-Calorie Diet and 6 Months of Weight Stability in Type 2 Diabetes: Pathophysiological Changes in Responders and Nonresponders
  • Calorie restriction for long-term remission of type 2 diabetes
  • Gastric bypass

    Bariatric Surgery A Systematic Review and Meta-analysis

    Hyperglycemia

    Fasting Insulin vs Hemoglobin A1c: Are We Getting It Right?

    Effect of Physiological Hyperinsulinemia on Gluconeogenesis in Nondiabetic Subjects and in Type 2 Diabetic Patients

    Diabetes and diet

    The Dilemma of Weight Loss in Diabetes



    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 Part 2–Board design and MOSFET testing…

    Having chosen MOSFETs, I went off to do some board design. I’m hoping this will be a very simple design; it needs to provide power to the ESP, connect ESP outputs to the driving MOSFETs, and provide connections for the loads to the MOSFETs.

    Here’s the schematic:

    image

    On the right we have all of the LOAD outputs; we’re using N-channel MOSFETS to switch to ground, so there are 8 outputs plus a ground. Somewhat conveniently – assuming I’ve read the data sheets right – there are 8 PWM outputs on the right side and 8 on the left.

    In my WS2811 extender I put both positive and negative terminals for the load on the board, but in this case I don’t have room so only the ground connections show up.

    The other two 9-pin connectors – ExtOut1 and ExtIn1 – are for a feature that I’m hoping will be very cool, but it will be oh-so-easier to explain when I have boards in hand.

    One question I already had was whether the ESP could put out enough current to switch the MOSFETs quickly enough. The time spent switching is time the MOSFETs spend in their linear region, and the Rds is much higher during that period. The SOT-32 package doesn’t give much opportunity for heat dissipation.

    I didn’t have any protoboards to mount the MOSFET on, but I did have some WS2812 LED boards that I made. Two of the solder pads matched and I used a short wire to hook on the third one.

    IMG_9584

    That’s wired up to the ESP.

    IMG_9582

    The ESP running very simple code that ramps up to full brightness and then back down.

    I then needed a test load. I don’t actually have a good 2-3 amp 5V test load, so this was my first test:

    IMG_9581

    That’s 5 of my ornament kits stacked on top of each other. At full brightness they are pulling just over an amp, which is my design point (more would be better). I let it ran on that for a few hours, and the MOSFET was maybe a little warmer than ambient, but barely. I threw on my 12V light bulb testing rig, and got 1.5 amps, and it was also fine with that. Two of those bulbs in parallel would unfortunately be 4 times the power which is more than the MOSFET is rated for, so I’ll need a different load to finish my testing.

    I am a little concerned that the ESP may have issues driving more than 1 channel as there could easily be 8 (or 16) channels trying to change all at once. The ESP has 16 independent PWM channels and I’m thinking that if I desync the frequencies slightly (say, 500 Hz, 501 Hz, etc.), the transition points for the PWM will generally not be at the same time.

    Anyway, I considered that enough of a test to do the board design. I had to do a custom component and footprint for the ESP because I couldn’t find one that matched my 30-pin DEVKIT board.

    One of these times I’m going to remember to do a video of the layout process, but I usually enjoy it so much that I don’t remember.

    image

    The MOSFETS live at the bottom to minimize the length of the traces that carry the most current, and to put them all near the bottom. The high-current traces are 1mm wide; I could likely go to 1.5 or even 2mm but that seems like a bit of overkill for the currents I expect. The driving traces from the ESP are 0.5mm because I want to get charge into and out of the gates as quickly as possible.

    There is a bit of creativity on the left side; the pins on the 9-pin connector are quite a bit offset from the ones on the ESP, so I took pin 12 and 13 and ran them up to the two top pins to make the rest easy to layout.

    The board is meant to be an “undershield”; I plan on putting female headers in the 15 pin connectors of this board and those will mate with the male headers that are already on the board. The power and load connectors should probably use right-angle headers.

    I spun a small order of these boards for testing.






    DORMA

    Preparation

    A few years ago, I got the idea of doing a solo ride along the RAMROD (my 2013 writeup here) route but in the reverse direction, which would obviously be named “DORMAR” (RAMROD backwards). It looked like it was doable from a water perspective, and would have the advantage of putting all of the climbing in the first 75 miles of the ride, rather than putting a huge climb in at 90 miles into the ride. That would be better because it would be cooler. And you could do *all* the climbing, including the last 600’ up to the Paradise visitor’s center which the Park Service no longer lets RAMROD do.

    I was excited enough to create a Facebook group and write a Rider’s Guide. Even scheduled a day for it.

    And, for two years, I found excellent reasons not to do the ride. Which means that I’m thinking that I really can’t finish the thing comfortably.

    This year, my lovely wife offered to drive SAG for me, which means I could ride it solo – which is really my preference – but without having to be wholly self-sufficient for the whole ride. My tentative plan was to meet her in Eatonville (115 miles in, after the majority of the climbing) to have a lunch, and then I would ride the last 40 miles.

    That was the working plan, and this time I actually came up with a date – the Monday after RAMROD, July 29th this year.

    Further planning commenced. And I realized that while I was really looking forward to the climbing part of the day – and the descents, I love the descents – I was unexcited about 40 miles of mostly flat riding in the heat of the afternoon after all the climbing. 3+ hours in the heat of the day after already doing all the hard work? I knew that I didn’t like the last 30 miles of RAMROD, and that at least was downhill (though with a headwind).

    Hmm. What would you call such a ride? It’s a little bit shorter than the DORMAR, so let’s chop a letter off of the name and we’ll call it “DORMA”. It will be 116 miles and over 8000’ of climbing.

    So, I spent a few hours creating the following in Inkscape:

    image

    I am *exceedingly* happy with how that turned out. It’s supposed to look something like this. For those of you who would prefer English, from left to right, Greenwater, Crystal Mountain Blvd, Cayuse Pass, Grove of the Patriarchs, Backbone Ridge, Inspiration Point, and Paradise (yes, it’s not really a Mont, but from the way climbs are named I think that makes the most sense).

    So, there’s a 32 mile climb from Enumclaw to the park entrance at Crystal Mountain, then a climb up Cayuse (1700’ over 6.1 miles), a climb up Backbone ridge (1330’ over 5.6 miles), and a climb up to Paradise (2621’ over 12.5 miles). None of the climbs are particularly steep – last weekend I did 4000’ of climbing at 9% – but there’s certainly a lot of it.

    For Kim to be SAG, she needs to have some idea of when I’m going to be at various points along the route. That is a bit of a challenge. For the main climbs, I actually have some data from last summer of the first climb, and it showed that I did that section in 50 minutes, which meant I was climbing at 34 feet per minute (at 195 watts, if you care). For the descents and easier climb at the beginning, I based it off other numbers I had or just a decent guess. That gave me the following timeline:

    image

    That’s 9:20 minutes total, 8:50 riding and :30 resting. That’s a little over 13 MPH average, and while my last run of RAMROD came in at an even 15 MPH, that included 50-60 paceline miles which bumps the average up a bit.

    I put this all together in a guide for my wife, along with a map of the Eatonville end location (the Visitor’s center that RAMROD uses as the first stop).

    T-1

    A list of tasks done in preparation for the ride, in random order:

  • Charging of electronics
    • Headlight to deal with the early start
    • Rear blinkie (these are technically required when riding in the park but are spottily enforced)
    • Di2 shifters (very happy to remember this)
    • MP3 player
  • Downloading of podcasts
  • Inflation of tires (80 PSI for my 700×28 Conti GP4s.)
  • Lubing of chain
  • Replacement of backup rear blinkie
  • Testing of backup backup rear blinkie
  • Clothing Selection
    • My best Castelli bibs
    • A Nike Dryfit underlayer (it’s not going to be very hot)
    • My Rails to Trials Jersey (a bit too big, but bright yellow, so good for visibility and not too hot. And really big pockets)
  • Pack bike bag (I have a cloth shopping bag that holds all of my basic riding stuff; arm/leg warmers (hope to skip these), light vests and coats, shoes, helmet, gloves, etc. This is a “grab and go” bag, so all I really needed to do was make sure I had my gloves and headband, which get hung up to dry after rides.
  • Tightened the BOA retention wheel on my left shoe
  • Food and drink prep (I don’t need much food these days, so it’s a lot more than I need)
  • Three servings of hydration mix (Bio Steel), one in a bottle and two in a pack.
  • A bag each of:
    • Mixed nuts
    • Trail mix
    • Cheez-its (my traditional long-ride fuel)
  • A bag of homemade jerky thawing in the fridge that I hope not to forget
  • A serving of SuperStarch to drink before I start.
  • Put sunscreen, chamois butt’r, and MP3 player/headphones in my small bag.
  • There are only 5 or so turns on the entire route and I have them in my head, so there’s no need to bring any maps or use my GPS to navigate. If I was doing the whole ride back to Enumclaw, I’d probably have done nav for the last section.

    I got a decent but not great night of sleep two nights before, and that is the one that really matters; the night before I typically never sleep well and since I’m getting up at 4AM I’m going to be messing with my REM sleep anyway.

    We headed down to Enumclaw, checked in at the Rodeway Inn, and went out for dinner at the Rainier Bar & Grill. I had a decent burger.

    From wakefullness to ridefullness

    After a malfunctioning AC and a hot crappy night of sleep, I woke up at 4AM for my 5AM departure. I don’t need that much time to get ready, but my eyes are much happier with contacts if I’m up for a bit.

    As part of getting ready, I took a look at the current weather. I had planned my gear based on mid-50s and then warming up as the morning went on – which for me would mean a vest and maybe arm warmers. Even though I’m wearing my “Rails to Trails” jersey which has giant pockets, I have a lot of stuff and clothes take up a lot of space, so I don’t want to take too much.

    51 degrees.

    Yikes. The logical thing to do would be to add my leg warmers, but leg warmers are really bulky and not easy to carry when you take them off (you can stuff them inside your bibs but they will keep you warmer than you like. So, it’s vest & arm warmers and hope that it warms up quickly.

    I drink 3 servings of SuperStarch mixed in water. SuperStarch is modified cornstarch, so go to your kitchen, take about 1/3 of a cup of cornstarch and add it to a glass of water, and drink it down. I’ll wait.

    Nasty, wasn’t it? But I’ve had good luck with it as a time-release glucose source.

    I glance at my watch as I ride out of the hotel parking lot, and it says 5:01. Perfect

    Enumclaw => Park Entrance

    32 miles, 2146’ of up

    As I head out on highway 410 – with front and back lights as it’s still *dark* – it’s a nice and still night and 51 degrees doesn’t feel as cold as I expected. I’m cold, but not that bad. Bodes well.

    The moon is out as a waning crescent; just the smallest slice bright and the rest slightly illuminated from earthshine. Pretty.

    The first 3 miles climbs about 500’, and I warm up pretty well during this section. There’s pretty much nobody at all out on the road; a few trucks heading to the gravel plant for the first mile and then it’s almost empty. I’m listening to Radiolab podcasts on this ride to keep me occupied (yes, I can still hear cars & trucks approaching), and after the first hill section I’m spinning along at about 170 watts. I want to keep a decent pace but I don’t want to use too much energy or legginess (legity), so I’m trying to stay in the sweet spot in between.

    After about an hour, I zip my vest back up because my hands are getting a bit cold. I flip over to another screen on my GPS to see the temperature…

    41.7

    Damn. I can tolerate the cold pretty well and my core temp is okay so far, but my knees do not like being this cold. Nothing to do but press on.

    The rest of this section passes slowly and it does warm up *slightly* as I keep going; the base of Crystal Mountain Blvd – where the National Park starts – is all the way up to 44 degrees.

    Cayuse Pass

    6.1 miles, 1700’ of up

    This section is a big misleading; the first two miles after you enter the park are the same gradient so you think it’s going to be easy, and then the pass begins.

    I haven’t been able to find out who designed Cayuse Pass, but he was pretty bull-headed. Starting at the top point, the route wraps around the hill contours but barely wavers from a constant 6%. That means it’s easy to find a groove and stick to it, but there’s pretty much no variety to be had. I’ve been eating a bit to keep my reserves up; a bit of trail mix and some Cheez-Its. And drinking, despite me not sweating much, as they last thing you want in the mountains is to get behind on hydration. There’s a nice view of the southeast side of the mountain at one point, but I keep climbing. The sun is up but I’m on the west side of the hills and therefore still in shadow.

    Eventually, I finally top out at the top into the sun and 47 degrees. My timeline estimate was that this would take me 3:20 and it actually took me 3:40, which I’ll note is pretty much exactly 10% slow. I was a little lower on wattage than I had expected, but it’s a long day and this is not the part of the ride to try to push.

    Cayuse Pass Descent

    11 miles, 2584’ of down

    The Cayuse descent is a bit like the climb I just finished; 8 miles of 6% and then a flatter 3 mile section. I really like mountain descents, the road is good, and the constant 6% gradient means that I can cruise along at around 31-32 MPH at about 150 watts. I rarely coast on downhills as spinning keeps my legs warm. There are two sections at the top that head eastish and are therefore in the sun, and I warm up a bit, but most of the route is once again on the west-facing side and are pretty cool. My next climbs are going to be on hills facing east, so I’ll have plenty of chance to warm up soon. A glorious 14 minutes of fast descending takes me to the runout section, and I get to the park entrance at 4:04 into the ride, or 24 minutes behind my estimate.

    A young park ranger takes my $15 – she does not offer the “Just go ahead” discount that I got when I climbed Sunrise last year – and I head to the Grove of the Patriarchs stop.

    If you are in the area, this is a great stop; the trees are truly massive and the short loop hike is worth the effort. I’m only here to use the bathroom and to refill my water bottles. My hydration state seems okay so I mix a bottle of BioSteel to replace the one I had just finished and fill my second bottle as much as possible – which is only about 50% given the water fountain stream. I drink a bit extra, refill it as much as possible, and head out after a quick 10 minute stop. 5 minutes of time made up on the stop.

    Backbone Ridge

    5.6 miles, 1330’

    This is the baby climb of the ride, but at 1330’ of up, it’s still quite a bit of vertical. I’m still trying to climb at a reasonable pace, and it seems that my pace today is a bit slow; it takes me 50 minutes to do this climb and I’m only climbing at 441 meters per hour; my usual rate is closer to 600 so this doesn’t bode particularly well. The climb isn’t very steep – only about 4% – and the temp is in the 50s and there are sections of sun. I feel decent, I’m just not riding very fast. Sometimes it happens.

    And my butt is hurting. I’ve had a saddle sore for a while, and it’s flaring up. That means I need to stand up every few minutes, which I do fairly often anyway to stretch my legs but not this much. Both of those are having an effect on my speed.

    On the way up there is a cycling group with matching jerseys that pass me going down. There are few cars.

    The descent is a fun one, and there’s a bit of flat. A miscalculation means that I’m done with RadioLab, so I switch over to music.

    My data says that I’m 22 minutes behind my timeline at this point, but other than a general sense of where I was at the top of Cayuse, I don’t know it at the time. It’s only relevant for Kim driving SAG, and there’s little I could do about it even if I knew.

    Paradise

    12.5 miles, 2621’

    This climb is 300’ shorter than the climb up to Sunrise that I’ve done a few times, and it’s dwarfed by the 5000’ Hurricane Ridge climb on the Olympic peninsula, but that’s still quite a bit of climbing.

    Nothing to do but HTFU and climb it. I feel decent but not strong, so I settle into a constant pace, which later data shows is a disappointing 170 watts.

    I climb, climb, and then I climb some more as I work my way up the ridge. After 46 minutes I take a quick stop to eat some jerky (on this climb I think trying to eat it on the bike will end up using it to decorate the roadway, providing an unexpected protein windfall to the local fauna). This is *not* my jerky – which I did manage to leave in the fridge at home – but a decidedly inferior substitute purchased at a gas station.

    Then it’s back on the bike to ride the rest of the way to the top. During the climb I stand up 19 times to rest my butt.

    Eventually I hit the switchbacks and reach a point where I can actually see the mountain, and then a bit more climbing and I reach Reflection Lakes where it flattens out, and then after a bit of downhill it’s just the short 600’ climb to the Paradise Visitor’s Center. Lots more traffic on that section and I’m pretty toasted, but after what feels like another 10 hours on the bike, at 11:45 I hit the top, where I get off my bike to fill my bottles, take a rest, and look at The Mountain. Despite it being a weekday, the place is packed.

    List of things on Eric that hurt:

    • Back
    • Butt
    • Feet
    • Toes
    • Knees
    • Pride

    My feet are really tender, likely from all the standing, and my knees – which pretty much never bother me even on really hilly courses – are hurting a lot. If I was smart, I’d take a couple of ibuprofen, but apparently I’m not.

    My target time for the climb is 80 minutes, and it ends up taking me 100 minutes, so about 20 minutes slow, or about 45 minutes behind in total. A little of that can be attributed to altitude; the average altitude of the climb was about 3750’, and – looking at some references on Alveolar O2 and altitude – I can calculate that I’m down about 13% on oxygen, and that goes up to 18% at the top.

    Math Pop Quiz:

    Q: It is currently 9:45. You are going to perform an activity that you expect to take 80 minutes. What will the time be when you finish? Please show your work.

    A: Well, 80 minutes is 1:20, so at means 10:65, but that’s not a real time, so normalize it to 11:05.

    Did you pass? I didn’t, as a look at my timeline will show that I ended up with 12:05 as my expected endpoint for this climb. So, rather than being 40 minutes late, I’m suddenly 20 minutes early.

    I obviously didn’t realize this at the time;I just knew that I had beat Kim to the top. I expect this will work out well, as she’s going to come up here and hang out a bit before following me down.

    I know you are wondering what my music was, so here’s the playlist I used from a number of years ago (it’s generated by my Personal DJ program):

    • My World – Avril Lavigne
    • Any Way You Want It – Journey
    • Be My Girl – The Police
    • Holiday – Scorpions
    • Silicon World – Eiffel 65
    • Roll the Bones – Rush
    • Time  – Pink Floyd
    • Sister – Creed
    • When I Come Around – Green Day
    • You Give Me All I Need – Scorpions
    • Analog Kid – Rush
    • Bastille Day – Rush
    • Crystal Baller – Third Eye Blind
    • A Praise Chorus – Jimmy Eat World
    • Tie Your Mother Down – Queen
    • I’ll Be Over You – Toto
    • Doug’s First Job – Uncle Bonsai
    • Out of the Vein – Third Eye Blind
    • Stranger in Town – Toto
    • God of Whine – Third Eye Blind
    • Questioned Apocalypse – One Fell Swoop
    • Always Somewhere – Scorpions
    • Wake Me Up When This Climb Ends – Green Day
    • Warning – Green Day
    • Summer Song – Joe Satriani
    • Cult of Personality – Living Colour
    • Sing Child – Heart
    • Suite Madame Blue – Styx
    • Trees – Tripod
    • It’s Easy (taking it pitch by pitch) – Boston
    • Going To California – Led Zeppelin

      After a bit of sitting and resting, I pick all the cashews out of the mixed nuts I brought, eat three brazil nuts, and head out for the rest of the ride.

      Descent and runout…

      43.8 miles, 5331’ down, 725’ up.

      It starts with about 11 miles of 4% grade and then gets flatter as the section goes on.

      As previously noted, several body parts are painful, but the first part of the descent is what I expect it to be; fast parts with some tight technical turns where I show that I am not the fastest descender in the peleton. As I turn off the top part, Kim passes me going up.

      The descent is just what I thought it would be, and would be fun to ride.

      Except. For. The. Headwind.

      One of the truisms of RAMROD is that the ride back to Enumclaw is always windy. I don’t know if that’s a truism here, but it’s certainly true today. It ranges from a few MPH when I’m in the forest to gusty sections that remind me of the time I rode part of the Kona Ironman course on the Big Island. On an average, it’s cutting off 5-10 MPH from my speed, which is just pissing me off. All the effort to climb up and then I get ripped off going down. When I finally get back outside the park, I text Kim so she has a time check for when I hit a certain part of the course.

      I have Elbe on my mind. On the north side of the road at the west end of town, there’s a gas station with attached store, and there’s an ice cold Coke Zero in one of their coolers, calling my name.

      The wind is doing nothing to improve my mood or reduce the pain in my body, but I can still ride and my power levels aren’t horrible. As a ride leader, I spend a lot of time out in the wind, but this one is nasty and relentless. I pass Ashland and it gets better for a couple minutes, but then comes back with a vengeance. 104 miles is my target, and I slowly watch the miles count up. At 100 miles my GPS loses the decimal point and time slows down.

      I finally get to Elbe, where I get the aforementioned Coke Zero and read the advertisements on the community bulletin board. Good price for tree grinding. I text Kim again, and then head inside to get an ice cream bar. I want something simple, but I end up with a Heath bar crunch. I haven’t eating an ice cream bar in a *long* time. It is sickeningly sweet and not very appealing, but I was raised to eat the food I took, so I finish it and feel a bit queasy.

      I’m getting ready to head out for the the last 12 miles, but I receive word that the organizers have decided to neutralize the remainder of the route, so I instead wait for my team car to show up, and we head to Eatonville to have a nice lunch at the Cottage Bakery and Cafe.

      Thoughts and other stuff

      That was a really hard ride, though if my knees/butt/feet were better, the section to Eatonville would have been simple as it was only another 12 miles.

      I tend to do my long rides solo, but at this distance companionship would have been welcome, though my climbing pace might have been problematic. Omitting the ride from Eatonville back to Enumclaw was a good decision, given my current fitness level. It was fun, for “long ride in the mountains” levels of “fun”.

      Stats:

      Distance: 103.36 miles
      Riding Time: 8:02:11
      Elapsed Time: 8:30:50
      Speed 12.9 MPH
      Power 146W
      Calories 4217
      Whines 3845

      My food for that day was three servings of SuperStarch before the ride, a handful of trail mix, half a package of jerky, 15 cashews, and about 50 cheez-its.

      Stava here, RideWithGPS route here.





        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.


        Pages:1234567...32