Issue #245: Allow control of more than one on-board pixel via `led` commands

Allow control of more than one on-board pixel via led commands:


Worst case power draw from Pixels

_(A pixel is the hardware unit, which contains three leds ... one red, one green, one blue. Avoids ambiguity between per-pixel vs. per-LED values)_

Note that these calculations are all estimates. For original WS2812 5050 pixels, a rule of thumb was 60mA current per pixel. The BusPirate 5, for example, uses SK6812 … where the last two digits indicate 12mA per led, with ~1mA overhead (idle power draw) per pixel, giving maximum power draw per pixel of ~38mA. I’ll show values for both below.

The buspirate system configuration settings only support three LED brightness scales:

  • 1/3 brightness
  • 1/5 brightness
  • 1/10 brightness

Thus, for worst case / highest power draw calculations, we’ll consider 1/3 brightness. The brightness reduction in the buspirate code is a linear reduction … which is to say, no gamma is applied anywhere in the current buspirate code. Therefore, the maximum value for R, G, and B is 255 / 3 == 81.

Pixel Type Per-Pixel mA Pixel Count Max Brightness approx max mA Notes
WS2812 5050 60 18 255 1080 No reduction
WS2812 5050 60 18 81 ~344 1/3 brightness
WS2812 5050 60 18 51 ~216 1/5 brightness
WS2812 5050 60 18 25 ~106 1/10 brightness
SK6812 38 18 255 684 No reduction
SK6812 38 18 81 ~218 1/3 brightness
SK6812 38 18 51 ~137 1/5 brightness
SK6812 38 18 25 ~67 1/10 brightness

Even at the maximum 1/3 brightness configuration, all pixels at 100% white would still leave > 150mA (> 280mA) for the rest of the system.

I encourage setting to 1/5 or 1/10 brightness in the settings … reduces the power fluctuations, even if your eyes don’t hurt from the brightness at the default 1/3 power. :wink:


Based on the above calculations, I think it’d be safe to allow updates to more than one onboard pixel, so long as the total current is below a pre-selected threshold.


Pseudo-code

bool estimated_to_exceed_power_limit(CPIXEL_COLOR* buffer, const size_t buffer_count) {

    static const uint8_t LEDS_PER_PIXEL = 3; // implied by CPIXEL_COLOR structure
    static const uint8_t MAX_DRAW_PER_LED_mA = 12; // SK6812 ... last two digits indicate 12mA per LED
    static const uint8_t MAX_DRAW_PER_PIXEL_mA = LEDS_PER_PIXEL * MAX_DRAW_PER_LED_mA;
    static const uint8_t IDLE_DRAW_PER_PIXEL_mA = 2; // SK6812 datasheet lists 1mA, real-world suggests 2mA
    static const uint8_t OVERALL_MARGIN_mA = 20; // safety margin?
    static const uint32_t MAXIMUM_ALLOWED_mA = 350 - (IDLE_DRAW_PER_PIXEL_mA * COUNT_OF_PIXELS) - OVERALL_MARGIN_mA ;

    uint32_t color_data_sum = 0;
    bool result = true; // exceeds result unless calculations will show otherwise
    for (uint_fast8_t i = 0; i < buffer_count; ++i) {
        static_assert(LEDS_PER_PIXEL == 3, "Need to update if supporting other color limits");
        color_data_sum += buffer[i].r;
        color_data_sum += buffer[i].g;
        color_data_sum += buffer[i].b;
    }
    if ((UINT32_MAX / MAX_DRAW_PER_LED) >= color_data_sum) {
        // no overflow when multiplying ... can continue
        uint32_t estimated_power_draw = (color_data_sum * MAX_DRAW_PER_LED) / 255;
        result = estimated_power_draw >= MAXIMUM_ALLOWED_mA;
    }
    // todo: logging when result is true
    // todo: automatic brightness limiter ... automatically reduce values to fit the current limit
    return result;
}

Issue opened by: henrygab

2 Likes

I will need to validate the data sheet and then test against the actual current consumption. I don’t trust the random LEDS that carry that part number to be accurate.

1 Like

Yes, you are right. Testing of actual draw of parts used is required before using the 38mA per pixel value.

At the same time, no WS2812 I’ve encountered exceeded the 60mA per pixel value. It’s likely safe to use that as the per-pixel maximum, even if later reducing to 38mA per pixel.

If you test actual power draw, please check the draw for each of the following RGB values:

  • 0x000000FF ( B)
  • 0x0000FF00 ( G )
  • 0x00FF0000 (R )
  • 0x0000FFFF ( GB)
  • 0x00FF00FF (R B)
  • 0x00FFFF00 (RG )
  • 0x00FFFFFF (RGB)

Bonus if testing some additional variations, replacing any 0xFF value above with:

  • 0x7F
  • 0x3F
  • 0x1F
  • 0x0F

Why: Variations in hardware may give surprise results. :cry: But, having the tested values will increase confidence not only for full-on values, but also for partial-values.

Even if get the variant with surprising results, there’s still hope. For example, the inner loop might be changed to sum( per-pixel, take the max of the R, G, and B components), and then multiply that result by the per-pixel maximum current (and divide by 255).

Note that I don’t expect the surprising results… SK6812 have become quite standard.

2 Likes

I tested things out with a USB PD monitor. But I believe it doesn’t have enough resolution. Here are my test results:

  • HiZ mode: 0.11-0.12 A
  • 0x000000 ( ): 0.10-0.11 A
  • 0x0000FF ( B): 0.11 A
  • 0x00FF00 ( G ): 0.11 A
  • 0xFF0000 (R ): 0.11 A
  • 0x00FFFF ( GB): 0.11 A
  • 0xFF00FF (R B): 0.11 A
  • 0xFFFF00 (RG ): 0.11 A
  • 0xFFFFFF (RGB): 0.11 A

I may go ahead and buy a cheap USB cable to be able to run these tests with my multimeter. But it may take a day or two.

1 Like

Was that when lighting up all 18 pixels with those values? If so, something is likely off…

18x SK6812 pixels set to full-white (with 1/3 brightness applied by BP), should have increased power draw by ~0.20A.

The simplest way I know to do this is:

Config
    -> LED Brightness (30%)
    -> LED Effect (Solid Color)
    -> LED Color (Red)
    -> Exit

Then, modify bpconfig.bp, setting led_color, which is stored as a hex string, and reboot.

1 Like

Nope, this test was done with the stock firmware with only one LED.

The thing is the power consumption is not too bad. If 1/3 brightness of 18 LEDs is 0.2 A, then all LEDs at full brightness is 0.6 A. That should be within the power budget of the USB port.

I ran some extra tests with your suggestion. Brightness set to 30%, effect is solid, here are the values:

  • Red: 0.17-0.18 A
  • Orange: 0.17-0.18 A
  • Yellow: 0.18-0.19 A
  • Green: 0.17 A
  • Blue: 0.17-0.18 A
  • Purple: 0.17-0.18 A
  • Pink: 0.17-0.18 A
  • White: 0.13-0.14 A
  • Disabled: 0.10 A

I think there’s some sort of scaling going on on top of the brightness as ~40 mA for all LEDs at 30% doesn’t make sense. For white, led_color is set to 0x282828 in BPCONFIG.BP file. If full brightness is 0xFF=255 and the value we have is 0x28=40, the LEDs are running at ~15.7% brightness. This means all LEDs set to white at 100% brightness should consume ~255 mA. We have base current consumption of ~100 mA, so the total should be ~355 mA for BPv5.

Of course somebody should check my math. I can do more tests if needed. Should be able to get an extra USB cable over the weekend.

2 Likes

Looking at a few datasheets, it does look like the SK6812 (clones) are actually limited to 12mA per die. Which is great news! I assumed the random small factory serial LEDs were all over the place.

1 Like

Is that 12 mA per LED or per color on the LED?

If it’s per LED, then total consumption should be 216 mA for all 18 LEDs. That’s close to my results during the final test.

Scratch that. I did one final test. I edited the BPCONFIG.BP file and set these values:

"led_effect": 1,
"led_color": "0xFFFFFF",
"led_brightness_divisor": 1,

the current consumption was 0.72 A. If I change led_brightness_divisor to 3, then the current consumption is 0.30-0.31 A. If I change led_brightness_divisor to 2, then the current consumption is 0.41-0.42 A. So that means at full brightness LEDs are consuming about ~620 mA, a single LED consumes ~35 mA. That means a single color die is limited to 12 mA. We need the scaling factor to be set to at least 2.

1 Like

That seems correct, but I suppose this is a time to be pedantic :wink:

12mA per LED die in each pixel. 12mA * 3 LED dies = 36mA for one pixel at full brightness.

With 18 pixels at full brightness 648mA.

Assuming your real world testing agrees, that is :slight_smile: I had always assumed sk6812 was just a generic part number with a wild west ecosystem of what you actually get as the controller chip. It seems, at least with the parts we buy, that they actually are intended to be limited to 12mA per LED die.

2 Likes

I’d say the latest test matches with the datasheet, previous test was tainted by multiple scaling factors within the firmware: led_brightness_divisor[1] and led_color [2] come into play for power consumption. As for the first test, it seems like we use the led_brightness_divisor [3] again, which I had no idea about. So that’s why for white at full brightness our LED current consumption was ~10 mA.

Our minimum led_brightness_divisor can be set to 3 via UI, that means even if we light up all 18 LEDs, we should be under the current limit of a USB port.

[1] BusPirate5-firmware/src/ui/ui_config.c at 1c251129fa12ca9ca24f3e2b5cb99af62f0105e9 · DangerousPrototypes/BusPirate5-firmware · GitHub
[2] BusPirate5-firmware/src/ui/ui_config.c at 1c251129fa12ca9ca24f3e2b5cb99af62f0105e9 · DangerousPrototypes/BusPirate5-firmware · GitHub
[3] BusPirate5-firmware/src/pirate/rgb.c at 1c251129fa12ca9ca24f3e2b5cb99af62f0105e9 · DangerousPrototypes/BusPirate5-firmware · GitHub

2 Likes

Good catch. At one point the LED was updated directly and pushed out the pio. I see there is a master processing function now that applies the divider even in led mode. That should be safe then. We can even force the divider upon entering led mode and restore after.

I also see led is dma to pio now. Which genius added that flair? I’m guessing Henry because I don’t remember doing it and DMA is a struggle for me :slight_smile:

1 Like

Yes, the firmware has a mandatory divisor, and you can’t bypass it via manually editing the configuration file. This is why I scaled by 81 / 255 in the table. And as you discovered, the config file stores the color directly … you can set any RGB value.

It seems you confirmed the values for full-on / white / 0x00FFFFFF aligned with the expected 0.12mA per LED (0.36mA per pixel). :tada:

I believe that you did do this, Ian. :slight_smile:


  • 18x pixels on white, with 1/3 limiter ~= 218mA (see table in first post / github).
  • BP5 idle uses ~120mA
  • Default current limiter for W is 300mA

Thus, with default settings, a BP5 might pull >640mA from the USB port for its current limiter kicks in…


Options (long-term):

  1. Have W/w commands announce its current limiter changes (before raising / after lowering), and have rgb.c dynamically adjust.
  2. Reduce limit in rgb.c to ~150mA.
  3. Do nothing.
2 Likes

I think I know what you mean.Something like

...
uint32_t led_brightness_divisor = max(system_config.led_brightness_divisor, 3);
c.r = c.r / led_brightness_divisor;
c.g = c.g / led_brightness_divisor;
c.b = c.b / led_brightness_divisor;

should do the trick maybe? Scratch that, update_pixels is not the proper function to set this scaling factor. Probably rgb_put is a better location for this. We’ll touch this function when we enable controlling multiple onboard LEDs anyways.

Git blame shows that you committed the change. Man you need a vacation! :smiley:

@henrygab do we use W/w in onboard LED mode? Do we need to? Maybe we can just disable that command? That way we’re limited to ~340 mA which is BP5 idle and LEDs. What are your thoughts?

2 Likes
  1. No, I do not recommend restricting the W/w commands.

  2. Rather, I recommend restricting the onboard pixel outputs.

    • When W increases the current limit, a synchronous “event” is generated(*) before the increase occurs
    • When W decreases the current limit (or w disables power output), an “event” is generated after the decrease is applied.
    • The pixel code would handle this event, dynamically limiting power.
    • Synchronous events are OK because this is not timing-sensitive (responding to user input).
    • See my crossing-in-the-ether post. :slight_smile:
  3. How to dynamically limit the on-board pixel power…

Are you sorry you asked yet? :wink:

2 Likes

I’d rather a warning when entering the mode or enabling the power supply, than disable a feature that users expect. Just because there is a potential danger of overloading the USB port (should result in disconnected device rather than damage, hopefully) some may have a valid use for the PSU in led mode.

Another option is to lower the max current in onboard led mode…

I see Henry replied and I need to digest that. But these are my initial thoughts.

2 Likes

Dynamic limitation of the power used has more complete solutions in both WLED and FastLED:

The BusPirate code can be simpler, as it only needs to support SK6812 for the onboard pixels, and can rely on the existing current limiter for pixels powered through the plank connector.

Comment by: henrygab

Let me attempt to simplify:

We want the BusPirate to fit within the power budget of the USB port. We’ll use 500mA as the safe default.

Excluding planks, the BusPirate uses some amount of power for the LCD, CPU, and components. For discussion purposes, let’s call this 150mA.

This would leaves 350mA power budget for everything else … planks and onboard pixels.

When w, the onboard pixels can essentially use that full 350mA.

When W, the onboard pixels should automatically self-limit themselves to what the plank might use.

Put another way, the on-board pixels should always be considered a lower priority than the powering the plank.

Is that clearer? Everything else is just the technical “how-to”…

(Yes, there would need to be some measurements of what the power budget could / should be…)

1 Like

At some point I did an extensive power budget for the 5+ design. It may be in git or on the old laptop. It was wrong because I assumed each pixel could use up to 60ma as a safety measure.

What I destinctly remember is that the second biggest user of current on the board (usually) is… 60ma for the LCD backlight :upside_down_face:

1 Like

If you want to prioritize this, and still have valid power budget for the rest of the system for the different BP models, it should be possible to implement quickly based on the notes I left above.

I am not yet volunteering, because while I could write the code quickly, I could not test / validate, and do not have the setup for easily measuring the power draw.

Plus, I’m trying to improve the command parsing; I can do anything, but I cannot do everything. :slight_smile:

1 Like

Updated pseudo-code for SK6812 values (12mA per led / 36mA per pixel), and changed the other variables to reflect power budget view.

Comment by: henrygab

2 Likes