11#if SKETCH_HAS_LARGE_MEMORY
97 weights[3] = white != 0 ? white : 255;
98 }
else if (count == 5) {
113#if SKETCH_HAS_LARGE_MEMORY
114static constexpr fl::size kPowerScalingTableSize = 256;
119struct PowerScalingState {
120 fl::array<fl::u8, kPowerScalingTableSize>
forward;
121 fl::array<fl::u8, kPowerScalingTableSize>
reverse;
131 for (fl::size i = 0; i < kPowerScalingTableSize; ++i) {
145static void rebuild_power_scaling_tables(
float exponent)
FL_NO_EXCEPT {
146 PowerScalingState&
state = gPowerScaling();
147 if (!(exponent > 0.0f) ||
149 state.reset_identity();
154 state.forward[0] = 0;
155 for (fl::size i = 1; i < kPowerScalingTableSize; ++i) {
156 float normalized =
static_cast<float>(i) / 255.0f;
161 int mapped =
static_cast<int>(
165 state.forward[255] = 255;
167 for (fl::size i = 1; i < kPowerScalingTableSize; ++i) {
169 if (step >
state.max_step) {
177 state.reverse[0] = 0;
179 for (
int scaled = 1; scaled < static_cast<int>(kPowerScalingTableSize); ++scaled) {
180 while (source < 255 &&
state.forward[source + 1] <= scaled) {
183 state.reverse[scaled] =
static_cast<fl::u8>(source);
189#if SKETCH_HAS_LARGE_MEMORY
197#if SKETCH_HAS_LARGE_MEMORY
198 return gPowerScaling().reverse[scaled_brightness];
200 return scaled_brightness;
206#if !FL_PLATFORM_HAS_TINY_MEMORY
208#if SKETCH_HAS_LARGE_MEMORY
209 return gPowerScaling().max_step;
238#define POWER_DEBUG_PRINT 0
249 fl::u32 red32 = 0, green32 = 0, blue32 = 0;
252 for(
size_t i = 0; i <
leds.size(); i++) {
273 if (emitter_count < 3 || emitter_count > 5 ||
274 codes.size() % emitter_count != 0) {
279 fl::u32 sums[5] = {};
280 for (fl::size i = 0; i < codes.size(); i += emitter_count) {
281 for (
fl::u8 channel = 0; channel < emitter_count; ++channel) {
286 (codes.size() / emitter_count);
287 for (
fl::u8 channel = 0; channel < emitter_count; ++channel) {
288 total += (sums[channel] * weights[channel]) >> 8;
328 if (!
rgbw.active() || white_mW == 0) {
330 FL_WARN_ONCE(
"power: controller is in RGBW mode but the power model "
331 "declares no white emitter, so the budget covers three "
332 "of its four diodes. Call "
333 "FastLED.setPowerModel(PowerModelRGBW(...)) to fix it.");
338 fl::u32 red32 = 0, green32 = 0, blue32 = 0, white32 = 0;
339 for (fl::size i = 0; i <
leds.size(); i++) {
340 fl::u8 r = 0, g = 0, b = 0, w = 0;
359 return red32 + green32 + blue32 + white32 +
392 for (
fl::u8 bit = 0; bit < 8; ++bit) {
401 return r >= num ? q : q - 1;
411 const fl::u32 requested_mW =
413 if (requested_mW <= max_power_mW) {
414 return target_brightness;
416 if (controllable_mW == 0 || max_power_mW <= fixed_mW) {
435 const fl::u32 headroom_mW = max_power_mW - fixed_mW;
438 if (allowed_scaled > target_scaled) {
439 allowed_scaled = target_scaled;
441 const fl::u32 recommended_scaled =
static_cast<fl::u32
>(allowed_scaled);
452 while (recommended > 0 &&
467 const fl::u32 controllable_mW = total_mW > fixed_mW ? total_mW - fixed_mW : 0;
488#if FL_COLOR_PIPELINE_SHARED
496 if (pipeline && hooks.unscaledPowerMilliwatts !=
nullptr) {
500 ? 5 : (
controller.getRgbw().active() ? 4 : 3);
501 return hooks.unscaledPowerMilliwatts(
520#if FL_PLATFORM_HAS_TINY_MEMORY
539#if SKETCH_HAS_LARGE_MEMORY
545 "dither reserve must fit 32 bits on 16-bit-size targets");
549 fl::u32 lit_r = 0, lit_g = 0, lit_b = 0;
550 for (fl::size i = 0; i <
leds.size(); ++i) {
552 lit_r +=
leds[i].r != 0 ? 1u : 0u;
553 lit_g +=
leds[i].g != 0 ? 1u : 0u;
554 lit_b +=
leds[i].b != 0 ? 1u : 0u;
567 return reserve > 0xFFFFFFFFu ? 0xFFFFFFFFu :
static_cast<fl::u32
>(reserve);
574#if FL_COLOR_PIPELINE_SHARED
585 for (fl::size i = 0; i <
leds.size(); ++i) {
586 lit += (
leds[i].r |
leds[i].g |
leds[i].b) != 0 ? 1u : 0u;
589 ? 5 : (
controller.getRgbw().active() ? 4 : 3);
592 fl::u32 sum_weights = 0;
593 for (
fl::u8 i = 0; i < emitters; ++i) {
594 sum_weights += weights[i];
596 const fl::u64 per_code =
static_cast<fl::u64
>(lit) * sum_weights;
598 return reserve > 0xFFFFFFFFu ? 0xFFFFFFFFu
599 :
static_cast<fl::u32
>(reserve);
602#if !defined(FL_IS_AVR)
614#if FL_COLOR_PIPELINE_SHARED
623 const fl::u32 count =
static_cast<fl::u32
>(
controller.size());
628 fl::u64 sums[5] = {};
632 for (fl::u32 i = 0; i < count; ++i) {
635 pixels.loadAndScaleRGBWWUnordered(
controller.getRgbww(),
636 &codes[0], &codes[1], &codes[2], &codes[3], &codes[4]);
637 }
else if (emitters == 4) {
638 pixels.loadAndScaleRGBWUnordered(
controller.getRgbw(),
639 &codes[0], &codes[1], &codes[2], &codes[3]);
641 pixels.loadAndScaleRGB(&codes[0], &codes[1], &codes[2]);
645 (source.r | source.g | source.b) != 0 &&
brightness != 0;
646 for (
fl::u8 c = 0; c < emitters; ++c) {
647 const fl::u8 upper = dithered
648 ?
static_cast<fl::u8>(codes[c] > 253 ? 255 : codes[c] + 2)
652 pixels.advanceData();
655 for (
fl::u8 c = 0; c < emitters; ++c) {
656 total += (sums[c] * weights[c] + 255u) >> 8;
658 return total > 0xFFFFFFFFu ? 0xFFFFFFFFu :
static_cast<fl::u32
>(total);
661struct ManagedFramePower {
662 fl::shared_ptr<fl::StreamingPipelineQ16> pipeline;
663 fl::ManagedPowerHistogram histogram;
664 fl::PowerCodecPolicy codec;
668struct FramePowerSnapshot {
669 fl::vector<ManagedFramePower> managed;
670 fl::vector<fl::CLEDController*> legacy;
673bool captureFramePowerSnapshot(FramePowerSnapshot* snapshot)
FL_NO_EXCEPT {
674 const fl::PowerFrameHooks& hooks = fl::powerFrameHooks();
675 fl::size managed_count = 0;
676 fl::size legacy_count = 0;
680 if (fl::channelColorPipeline(*
controller) && hooks.buildPowerHistogram) {
688 snapshot->managed.resize(managed_count);
689 snapshot->legacy.resize(legacy_count);
690 if (snapshot->managed.size() != managed_count ||
691 snapshot->legacy.size() != legacy_count)
return false;
692 fl::size managed_index = 0;
693 fl::size legacy_index = 0;
699 if (pipeline && hooks.buildPowerHistogram !=
nullptr) {
700 if (managed_index >= managed_count)
return false;
701 ManagedFramePower& entry = snapshot->managed[managed_index++];
702 entry.pipeline = pipeline;
707 hooks.buildPowerHistogram(
710 emitters, &entry.histogram);
712 if (legacy_index >= legacy_count)
return false;
713 snapshot->legacy[legacy_index++] =
controller;
717 return managed_index == managed_count && legacy_index == legacy_count;
720fl::u32 framePowerAtFluxMilliwatts(
const FramePowerSnapshot& snapshot,
725 static_cast<fl::i32
>(flux_q16));
726 const fl::PowerFrameHooks& hooks = fl::powerFrameHooks();
727 for (fl::size i = 0; i < snapshot.managed.size(); ++i) {
728 const ManagedFramePower& entry = snapshot.managed[i];
729 const fl::u64 numerator = hooks.histogramPowerNumerator(
730 *entry.pipeline, entry.histogram, flux, entry.codec,
734 entry.histogram.pixels + ((numerator + 255u) >> 8);
736 for (fl::size i = 0; i < snapshot.legacy.size(); ++i) {
737 total += legacyFramePowerMilliwatts(*snapshot.legacy[i],
740 return total > 0xFFFFFFFFu ? 0xFFFFFFFFu :
static_cast<fl::u32
>(total);
749 FramePowerSnapshot snapshot;
750 if (!captureFramePowerSnapshot(&snapshot)) {
754 plan.legacy_brightness = 0;
755 plan.modeled_mW = 0xFFFFFFFFu;
756 plan.infeasible =
true;
757 plan.limited = requested_brightness != 0;
761 requested_brightness).
rawQ16();
762 const fl::u32 zero_demand = framePowerAtFluxMilliwatts(snapshot, 0, 0);
763 if (zero_demand > budget_mW) {
765 plan.legacy_brightness = 0;
766 plan.modeled_mW = zero_demand;
767 plan.infeasible =
true;
768 plan.limited = requested_brightness != 0;
772 fl::u32 high = request_flux;
773 const auto legacy_at_flux = [request_flux, requested_brightness](
774 fl::u32 candidate) ->
fl::u8 {
775 return candidate == request_flux ? requested_brightness
776 :
static_cast<fl::u8>((
static_cast<fl::u64
>(candidate) * 255u) >> 16);
779 const fl::u32 candidate = low + (high - low + 1) / 2;
780 if (framePowerAtFluxMilliwatts(snapshot, candidate,
781 legacy_at_flux(candidate)) <= budget_mW) {
784 high = candidate - 1;
788 plan.limited = low < request_flux;
792 plan.legacy_brightness = legacy_at_flux(low);
793 plan.modeled_mW = framePowerAtFluxMilliwatts(
794 snapshot, low, plan.legacy_brightness);
798fl::u32 fl::framePowerMCUBaselineMilliwatts()
FL_NO_EXCEPT {
805 if (p->getEnabled() && fl::channelColorPipeline(*p)) {
811void beginManagedPowerFrame(
fl::u8 requested_brightness, fl::u32 budget_mW,
814 fl::calculateFramePowerPlan(requested_brightness, budget_mW);
816 fl::PowerFrameHooks& hooks = fl::powerFrameHooks();
818 hooks.frameFluxActive =
true;
821 fl::PowerFrameHooks& hooks = fl::powerFrameHooks();
822 hooks.frameFluxActive =
false;
823 hooks.frameFluxQ16 = 0;
836FramePowerEndListener& framePowerEndListener()
FL_NO_EXCEPT {
837 static FramePowerEndListener listener;
841fl::u8 managedShowBrightness(
fl::u8 requested_brightness,
843 if (!hasManagedPowerChannel()) {
847#if FASTLED_HAS_ENGINE_EVENTS
851 endManagedPowerFrame();
859 fl::u8 legacy_brightness = requested_brightness;
860 beginManagedPowerFrame(requested_brightness, budget_mW,
862 return legacy_brightness;
866const fl::FramePowerDispatch* fl::framePowerDispatch()
FL_NO_EXCEPT {
867 static const FramePowerDispatch dispatch = {
868 &calculateFramePowerPlan, &managedShowBrightness,
870#if FASTLED_HAS_ENGINE_EVENTS
885 fl::u32 controllable_mW = 0;
889 const fl::u32 count = pCur->
size();
897 controllable_mW += unscaled_mW > dark_mW ? unscaled_mW - dark_mW : 0;
901#if POWER_DEBUG_PRINT == 1
902 Serial.print(
"power demand at full brightness mW = ");
903 Serial.println( fixed_mW + controllable_mW);
906 const fl::u32 requested_power_mW =
908#if POWER_DEBUG_PRINT == 1
909 if( target_brightness != 255 ) {
910 Serial.print(
"power demand at scaled brightness mW = ");
911 Serial.println( requested_power_mW);
913 Serial.print(
"power limit mW = ");
914 Serial.println( max_power_mW);
917 if( requested_power_mW <= max_power_mW) {
923#if POWER_DEBUG_PRINT == 1
924 Serial.print(
"demand is under the limit");
926 return target_brightness;
930 fixed_mW, controllable_mW, target_brightness, max_power_mW);
931#if POWER_DEBUG_PRINT == 1
932 Serial.print(
"recommended brightness # = ");
933 Serial.println( recommended_brightness);
935 fl::u32 resultant_power_mW =
937 Serial.print(
"resultant power demand mW = ");
938 Serial.println( resultant_power_mW);
949 return recommended_brightness;
962#if SKETCH_HAS_LARGE_MEMORY
963 rebuild_power_scaling_tables(model.exponent);
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
CLEDController * controller
static CLEDController * head() FL_NO_EXCEPT
Get the first LED controller in the linked list of controllers.
VIRTUAL_IF_NOT_AVR int size() const FL_NO_EXCEPT
How many LEDs does this controller manage?
CLEDController * next() FL_NO_EXCEPT
Get the next controller in the linked list after this one.
virtual void onEndFrame() FL_NO_EXCEPT
static void removeListener(Listener *listener) FL_NO_EXCEPT
static void addListener(Listener *listener, int priority=0) FL_NO_EXCEPT
static bool hasListener(Listener *listener) FL_NO_EXCEPT
static FluxScalar fromRawQ16(i32 raw) FL_NO_EXCEPT
A limiter fraction already in s16.16, clamped to [0, 1].
i32 rawQ16() const FL_NO_EXCEPT
static FluxScalar fromBrightness(u8 brightness) FL_NO_EXCEPT
setBrightness(b) as the linear flux scalar b/255.
A linear flux scalar in s16.16.
static T & instance() FL_NO_EXCEPT
fl::CLEDController CLEDController
#define BINARY_DITHER
Enable dithering using binary dithering (only option)
#define DISABLE_DITHER
Disable dithering.
Backward compatibility header - use fl/fastpin.h directly.
Functions for red, green, blue, white (RGBW) output.
void set_max_power_indicator_LED(fl::u8 pinNumber) FL_NO_EXCEPT
Select a pin with an LED that will be flashed to indicate that power management is pulling down the b...
fl::u32 scale_power_for_brightness(fl::u32 total_mW, fl::u8 brightness) FL_NO_EXCEPT
Applies the configured power-scaling response to a total power value.
fl::u8 calculate_max_brightness_for_power_vmA(const CRGB *ledbuffer, fl::u16 numLeds, fl::u8 target_brightness, fl::u32 max_power_V, fl::u32 max_power_mA) FL_NO_EXCEPT
Determines the highest brightness level you can use and still stay under the specified power budget f...
fl::u32 calculate_unscaled_power_mW(fl::span< const CRGB > leds) FL_NO_EXCEPT
Determines how many milliwatts the current LED data would draw at max brightness (255)
fl::u8 calculate_max_brightness_for_power_mW(const CRGB *ledbuffer, fl::u16 numLeds, fl::u8 target_brightness, fl::u32 max_power_mW) FL_NO_EXCEPT
Determines the highest brightness level you can use and still stay under the specified power budget f...
fl::u32 power_ratio_of_255(fl::u32 num, fl::u32 den) FL_NO_EXCEPT
min(255, floor(255 * num / den)) for den > 0, computed in 32-bit arithmetic.
fl::u32 dither_reserve_mW(fl::span< const CRGB > leds) FL_NO_EXCEPT
The most BINARY_DITHER can add to one latched frame of leds, in mW (#4342, contract R4: "quantization...
fl::u32 controller_dither_reserve_mW(const fl::CLEDController &controller) FL_NO_EXCEPT
dither_reserve_mW() for a controller that dithers, and zero for one that does not: a mode other than ...
fl::u32 controller_unscaled_power_mW(const fl::CLEDController &controller) FL_NO_EXCEPT
One controller's demand at full brightness, in mW, including idle draw – the quantity the limiter sum...
void set_power_scaling_exponent(float exponent) FL_NO_EXCEPT
Set a non-linear brightness-to-power response exponent.
PowerModelRGB get_power_model() FL_NO_EXCEPT
Get current RGB power model.
void set_power_model(const PowerModelRGB &model) FL_NO_EXCEPT
Set custom RGB LED power consumption model.
fl::u32 calculate_unscaled_emitter_power_mW(fl::span< const fl::u8 > codes, fl::u8 emitter_count) FL_NO_EXCEPT
Charge already-solved physical output bytes in source emitter order: RGB, RGBW, or RGB + warm white +...
float get_power_scaling_exponent() FL_NO_EXCEPT
Get the current brightness-to-power response exponent.
fl::u8 get_white_emitter_mW() FL_NO_EXCEPT
The white-emitter draw the caller declared, or zero if they declared only an RGB model.
Determines which platform system definitions to include.
#define FL_WARN_ONCE(...)
constexpr T && forward(typename remove_reference< T >::type &t) FL_NO_EXCEPT
void reverse(Iterator first, Iterator last) FL_NO_EXCEPT
void enable_rgbw_power_estimate() FL_NO_EXCEPT
Lets the power limiter charge a controller's RGBW conversion.
u8 u8 fract8 blur_amount FL_NO_EXCEPT
constexpr bool almost_equal(T a, T b, U tolerance) FL_NO_EXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type clamp(T x, T lo, T hi) FL_NO_EXCEPT
FASTLED_FORCE_INLINE void rgb_2_rgbw(const Rgbw &cfg, u8 r, u8 g, u8 b, u8 r_scale, u8 g_scale, u8 b_scale, u8 *out_r, u8 *out_g, u8 *out_b, u8 *out_w) FL_NO_EXCEPT
Converts RGB to RGBW using one of the functions.
ColorPipelineHooks & colorPipelineHooks() FL_NO_EXCEPT
The installed hooks.
constexpr enable_if< is_fixed_point< T >::value, T >::type step(T edge, T x) FL_NO_EXCEPT
PowerCodecPolicy powerChannelCodecPolicy(const CLEDController &controller) FL_NO_EXCEPT
Identify the channel's final current/quantization choice without adding a virtual slot or storage to ...
float powf(float base, float exponent) FL_NO_EXCEPT
long lroundf(float value) FL_NO_EXCEPT
Result of the shared frame-budget prepass on large-memory managed output.
Low level pixel data writing class.
Includes defintions for RGB and HSV pixels.
static constexpr fl::u32 kDitherReserveCodes
fl::conditional< sizeof(fl::size)<=2, fl::u32, fl::u64 >::type dither_acc_t
static constexpr float kLinearPowerScalingExponent
static rgbw_power_fn gRgbwPowerEstimate
static PowerModelRGB & gPowerModel() FL_NO_EXCEPT
Global RGB power model (initialized to WS2812 @ 5V defaults, linear response)
static void apply_rgb_power_model(const PowerModelRGB &model) FL_NO_EXCEPT
static constexpr float kPowerScalingExponentEpsilon
static fl::u8 max_power_step() FL_NO_EXCEPT
What one output code can cost at the steepest point of the response, in the 0-255 scaled units calcul...
static void emitter_power_weights(fl::u8 count, fl::u8(&weights)[5]) FL_NO_EXCEPT
static fl::u8 map_power_value(fl::u8 brightness) FL_NO_EXCEPT
static fl::u8 gMaxPowerIndicatorLEDPinNumber
static RgbwwEmitterPower & gRgbwwEmitterPower() FL_NO_EXCEPT
fl::u32(* rgbw_power_fn)(fl::span< const CRGB >, const fl::Rgbw &)
static fl::u32 fixed_power_mW(fl::u32 led_count) FL_NO_EXCEPT
static fl::u8 brightness_within_budget(fl::u32 fixed_mW, fl::u32 controllable_mW, fl::u8 target_brightness, fl::u32 max_power_mW) FL_NO_EXCEPT
static const fl::u8 gMCU_mW
static WhiteEmitterPower & gWhiteEmitterPower() FL_NO_EXCEPT
static fl::u8 unmap_power_value(fl::u8 scaled_brightness) FL_NO_EXCEPT
The white emitter's draw, when the caller declared one.
Functions to limit the power used by FastLED.
#define FL_STATIC_ASSERT(...)
Portable compile-time assertion wrapper.
fl::u8 blue_mW
Blue channel power at full brightness (255), in milliwatts.
fl::u8 dark_mW
Dark LED baseline power consumption, in milliwatts.
fl::u8 red_mW
Red channel power at full brightness (255), in milliwatts.
float exponent
Brightness-to-power response exponent (1.0 = linear)
fl::u8 green_mW
Green channel power at full brightness (255), in milliwatts.
RGB LED power consumption model Used for standard 3-channel LEDs (WS2812, WS2812B,...
RGBW LED power consumption model Used for 4-channel LEDs (SK6812 RGBW and similar).
fl::u8 white_mW
Cool white channel power at full brightness (255), in milliwatts.
fl::u8 green_mW
Green channel power at full brightness (255), in milliwatts.
fl::u8 warm_white_mW
Warm white channel power at full brightness (255), in milliwatts.
fl::u8 red_mW
Red channel power at full brightness (255), in milliwatts.
fl::u8 blue_mW
Blue channel power at full brightness (255), in milliwatts.
RGBWW LED power consumption model (RGB + warm white + cool white).