Proffiboard/OS power management

Hello,
yesterday I handed over the proffiboard-based lightsaber I assembled and programmed for my brother and Obiwan was very pleased. :wink:
However, upon doing several final user-specific adjustments to the modifications I integrated into ProffiOS 6.8(separate force-push/quote playing modes, hidden blade presets for special functions, boot volume setting etc.), I also measured the power consumption of the whole saber. To my surprise, the saber drew approximately 20 to 25 mA of current with the blade retracted and no active LEDs. This does not come even close to the IDLE endurance of many month, I read about in several saber forums. Since I suspected the motion chip to be still active in that state, but I did not need that, because the saber does not use gestures in off mode, I dug a little deeper. So, I discovered the IDLE_OFF_TIME and the MOTION_TIMEOUT defines. The first one just seems to disable accent LEDs and sound in off-mode, after a set amount of time. The second one just seems to be used in some special gesture heavy prop files like fett263 and sa22c, for the purpose to prevent the saber taking a power nap too early and thus not being sensitive to gestures when it should. So both do not seem to do what I want. Does anyone here know the intended way to put the proffiboard into kind of a standby mode where the power consumption is minimal (e.g. the motion chip off)? What power is it supposed to draw in that state? I remember one other saber of my brother needing an additional wake-up click of the power button if it is left off for 5 minutes or so. This is probably done by powering off everything unneeded on the board, kicking the STM32 into a sleep mode and attaching an interrupt for wake-up to one button.

Best regards,
DeFi

Do you have an OLED display?

If so, there was some new OLED low power mode code checked in at the beginning of the month.
Try the current Master version OS?

Proffieboards donā€™t have a ā€œdeep sleepā€ mode, they only have low-power standby modes. The pins that are capable of waking the board up donā€™t align well with other functions unfortunately, which makes deep sleep hard to implement on a V2 board. (That part at least should be rectified on a V3 board.)

Now, there may be many reasons why a board has higher standby power draw. Some of them are obvious, like a LED that never turns off, or a sound that never stops playing, but most of them at not nearly so simple.

One of the most common reasons is when you use a style that ProffieOS is unable to really figure out when it is off, and so the power for the blade remains on, even if the blade just shows black. The result is usually a power draw of about 1mA per pixel.

OLEDs is another thing that always draws power, but thatā€™s been fixed in ProffieOS 7.x.

TouchButtons will always draw some power, but I havenā€™t measured how much.

Motion staying on doesnā€™t draw that much power, I think itā€™s something like 3.5mA, however the cpu will also draw some more power.

Finally, itā€™s possible that something happened to the hardware that makes it draw more power, although that is probably the least likely of all of these thingsā€¦

Anyways, start by posting your config file, maybe it will be obvious whatā€™s doing it. If not, Iā€™m more than happy to help walk you through how to figure out what is drawing the power, and then we can see if we can fix it.

Thank you for your replies. Aside from the interface, my config is most likely nothing really special. I have a 130 LED blade in parallel with the 6 LED neopixel blade connector, an additional and separately controlled 6 LED neopixel connector at the tip of the chassis and two LEDs for kyber crystal accents, making a total of three logical blades. No OLED, no Touchbuttons or other extra stuff. If the saber is off, all LEDs are off, except for a boot time only battery indicator on the connector that lasts 4.5 seconds. During my current draw measurement, the blade was not even connected. Only the LEDs on the chassis, since I had to test outside of the hilt. My neopixels seem to draw 0.54mA per LED at 4V when not switched on, explaining at least 4,32mA of the power draw if the proffiboard does not cut power to them.

Ah, I know that from some ESP32 projects. XD

Hmmm, I have two rather fancy blade styles in some of the blades. However, only for the main blade which was not connected at the time of current testing.

I would also deem that very unlikely, but in the end you never know. I already had to deal with leaking LEDs, mosfets and ICs in other cases.

Here is a cleaned up version of the config where I removed some personal stuff, but left in all shenanigans I may have potentially dropped. The StyleSpecialPtr stuff is just a workaround for something I was not able to achieve with a usual style definition (I had just a few weekends to dig into ProffiOS stuff).

#ifdef CONFIG_TOP
#include "proffieboard_v2_config.h"

#define NUM_BLADES 3
#define NUM_BUTTONS 2
#define VOLUME 1000
#define BOOT_VOLUME 500
const unsigned int maxLedsPerStrip = 144;
#define CLASH_THRESHOLD_G 2.0
#define ENABLE_AUDIO
#define ENABLE_MOTION
#define ENABLE_WS2811
#define ENABLE_SD
#define DISABLE_DIAGNOSTIC_COMMANDS // free up some memory space
// filter low frequencies to protect the speaker
#define FILTER_CUTOFF_FREQUENCY 100
#define FILTER_ORDER 8
#define MAX_CYCLE_PRESET 4 // number of presets accessible in loop. remaining presets are hidden
#define FLASHLIGHT_STYLE 4 // the zero based index of the flashligth style
//#define BATTERY_STYLE 5 // the zero based index of the battery indicator style. enables visual battery indicator.

#define SAVE_VOLUME

#endif

#ifdef CONFIG_PRESETS

using BatteryLevelStyle = InOutHelperX<Gradient<Red,Orange,Yellow,Green,Green,Green>, BatteryLevel>;
using ObiwanBladeStyle = Layers<HumpFlicker<RgbArg<BASE_COLOR_ARG,Rgb<0,135,255>>,Mix<Int<24576>,Black,RgbArg<BASE_COLOR_ARG,Rgb<0,135,255>>>,60>,TransitionEffectL<TrWaveX<RgbArg<BLAST_COLOR_ARG,Rgb<255,255,255>>,Scale<EffectRandomF<EFFECT_BLAST>,Int<100>,Int<400>>,Int<100>,Scale<EffectPosition<EFFECT_BLAST>,Int<100>,Int<400>>,Scale<EffectPosition<EFFECT_BLAST>,Int<28000>,Int<8000>>>,EFFECT_BLAST>,Mix<IsLessThan<ClashImpactF<>,Int<26000>>,TransitionEffectL<TrConcat<TrInstant,AlphaL<RgbArg<CLASH_COLOR_ARG,Rgb<255,255,255>>,Bump<Scale<BladeAngle<>,Scale<BladeAngle<0,16000>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-12000>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<10000>>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-10000>>>,Scale<ClashImpactF<>,Int<12000>,Int<60000>>>>,TrFadeX<Scale<ClashImpactF<>,Int<200>,Int<400>>>>,EFFECT_CLASH>,TransitionEffectL<TrWaveX<RgbArg<CLASH_COLOR_ARG,Rgb<255,255,255>>,Scale<ClashImpactF<>,Int<100>,Int<400>>,Int<100>,Scale<ClashImpactF<>,Int<100>,Int<400>>,Scale<BladeAngle<>,Scale<BladeAngle<0,16000>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-12000>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<10000>>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-10000>>>>,EFFECT_CLASH>>,LockupTrL<TransitionEffect<AlphaMixL<Bump<Scale<BladeAngle<>,Scale<BladeAngle<0,16000>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-12000>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<10000>>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-10000>>>,Scale<SwingSpeed<100>,Int<14000>,Int<18000>>>,BrownNoiseFlickerL<RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>,Int<200>>,StripesX<Int<1800>,Scale<NoisySoundLevel,Int<-3500>,Int<-5000>>,Mix<Int<6425>,Black,RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>>,RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>,Mix<Int<12850>,Black,RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>>>>,AlphaL<AudioFlicker<RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>,Mix<Int<10280>,Black,RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>>>,Bump<Scale<BladeAngle<>,Scale<BladeAngle<0,16000>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-12000>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<10000>>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-10000>>>,Int<13000>>>,TrJoin<TrDelay<8000>,TrInstant>,TrFade<3000>,EFFECT_LOCKUP_BEGIN>,TrConcat<TrJoin<TrDelay<50>,TrInstant>,Mix<IsLessThan<ClashImpactF<>,Int<26000>>,RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>,AlphaL<RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>,Bump<Scale<BladeAngle<>,Scale<BladeAngle<0,16000>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-12000>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<10000>>>,Sum<IntArg<LOCKUP_POSITION_ARG,16000>,Int<-10000>>>,Scale<ClashImpactF<>,Int<20000>,Int<60000>>>>>,TrFade<300>>,TrConcat<TrInstant,RgbArg<LOCKUP_COLOR_ARG,Rgb<255,255,255>>,TrFade<400>>,SaberBase::LOCKUP_NORMAL>,ResponsiveLightningBlockL<Strobe<RgbArg<LB_COLOR_ARG,Rgb<255,255,255>>,AudioFlicker<RgbArg<LB_COLOR_ARG,Rgb<255,255,255>>,Blue>,50,1>,TrConcat<TrInstant,AlphaL<RgbArg<LB_COLOR_ARG,Rgb<255,255,255>>,Bump<Int<12000>,Int<18000>>>,TrFade<200>>,TrConcat<TrInstant,HumpFlickerL<AlphaL<RgbArg<LB_COLOR_ARG,Rgb<255,255,255>>,Int<16000>>,30>,TrSmoothFade<600>>>,LockupTrL<AlphaL<RandomPerLEDFlickerL<RgbArg<DRAG_COLOR_ARG,Rgb<255,255,255>>>,SmoothStep<IntArg<DRAG_SIZE_ARG,30187>,Int<3000>>>,TrConcat<TrJoin<TrDelay<4000>,TrWipeIn<200>>,AlphaL<BrownNoiseFlickerL<RgbArg<DRAG_COLOR_ARG,Rgb<255,255,255>>,Int<300>>,SmoothStep<IntArg<DRAG_SIZE_ARG,30187>,Int<3000>>>,TrFade<4000>>,TrFade<300>,SaberBase::LOCKUP_DRAG>,LockupTrL<AlphaL<Remap<Scale<RampF,Int<65536>,Int<0>>,StaticFire<Mix<TwistAngle<>,RgbArg<STAB_COLOR_ARG,Rgb<255,24,0>>,RotateColorsX<Int<3000>,RgbArg<STAB_COLOR_ARG,Rgb<255,24,0>>>>,Mix<TwistAngle<>,RotateColorsX<Int<3000>,RgbArg<STAB_COLOR_ARG,Rgb<255,24,0>>>,RotateColorsX<Int<3000>,Mix<Int<12000>,Black,RgbArg<STAB_COLOR_ARG,Rgb<255,24,0>>>>>,0,3,5,3000,10>>,SmoothStep<IntArg<MELT_SIZE_ARG,28000>,Int<4000>>>,TrConcat<TrWipeIn<100>,AlphaL<RgbArg<STAB_COLOR_ARG,Rgb<255,24,0>>,SmoothStep<IntArg<MELT_SIZE_ARG,28000>,Int<4000>>>,TrJoin<TrDelay<4000>,TrFade<300>>,AlphaL<Mix<TwistAngle<>,RgbArg<STAB_COLOR_ARG,Rgb<255,24,0>>,RotateColorsX<Int<3000>,RgbArg<STAB_COLOR_ARG,Rgb<255,24,0>>>>,SmoothStep<IntArg<MELT_SIZE_ARG,28000>,Int<4000>>>,TrFade<4000>>,TrWipe<200>,SaberBase::LOCKUP_MELT>,InOutTrL<TrWipeX<IgnitionTime<300>>,TrWipeInX<RetractionTime<0>>,Black>,TransitionEffectL<TrConcat<TrJoin<TrDelay<2000>,TrInstant>,AlphaL<Mix<BatteryLevel,Red,Green>,Bump<BatteryLevel,Int<10000>>>,TrFade<300>>,EFFECT_BATTERY_LEVEL>>;
using RainbowFireBladeStyle = Layers<StyleFire<Gradient<RotateColorsX<Variation,Red>,RotateColorsX<Variation,Orange>,RotateColorsX<Variation,Yellow>,RotateColorsX<Variation,Green>,RotateColorsX<Variation,Cyan>,RotateColorsX<Variation,Blue>,RotateColorsX<Variation,Magenta>>,Red,0,3,FireConfig<0,2000,5>,FireConfig<0,2000,5>,FireConfig<0,2000,5>,FireConfig<0,2000,5>>,TransitionEffectL<TrConcat<TrInstant,BrownNoiseFlickerL<AlphaL<White,Int<16000>>,Int<50>>,TrSmoothFade<600>>,EFFECT_LOCKUP_END>,ResponsiveLockupL<Strobe<White,BrownNoiseFlicker<White,Red,300>,50,1>,TrConcat<TrInstant,White,TrFade<200>>,TrFade<400>,Scale<BladeAngle<0,16000>,Int<4000>,Int<26000>>,Int<6000>,Scale<SwingSpeed<100>,Int<10000>,Int<14000>>>,ResponsiveLightningBlockL<Strobe<White,AudioFlicker<White,Blue>,50,1>,TrConcat<TrInstant,AlphaL<White,Bump<Int<12000>,Int<18000>>>,TrFade<200>>,TrConcat<TrInstant,HumpFlickerL<AlphaL<White,Int<16000>>,30>,TrSmoothFade<600>>>,ResponsiveBlastL<White,Int<400>,Scale<SwingSpeed<200>,Int<100>,Int<400>>,Int<400>>,ResponsiveClashL<White,TrInstant,TrFade<400>,Scale<BladeAngle<0,16000>,Int<4000>,Int<26000>>,Int<6000>>,ResponsiveStabL<Red,TrWipeIn<600>,TrWipe<600>>,ResponsiveDragL<BrownNoiseFlickerL<White,Int<300>>,TrWipeIn<400>,TrFade<400>>,ResponsiveMeltL<Mix<TwistAngle<>,Red,Orange>,TrWipeIn<600>,TrSmoothFade<600>>,InOutTrL<TrWipe<300>,TrWipeIn<500>,Black>>;

// by DeFi
// derived from StyleNormalPtr
// adds battery level indicator on boot
// Arguments: color, clash color, turn-on/off time
template<class base_color,
         class clash_color,
         int out_millis,
         int in_millis,
         class lockup_flicker_color = WHITE,
         class blast_color = WHITE>
StyleAllocator StyleSpecialPtr() {
#if 0
  typedef AudioFlicker<base_color, lockup_flicker_color> AddFlicker;
  typedef Blast<base_color, blast_color> AddBlast;
  typedef Lockup<AddBlast, AddFlicker> AddLockup;
  typedef SimpleClash<AddLockup, clash_color> AddClash;
  return StylePtr<InOutHelper<AddClash, out_millis, in_millis> >();
#else
 typedef Layers<base_color,
                SimpleClashL<clash_color>,
                LockupL<AudioFlickerL<lockup_flicker_color> >,
                BlastL<blast_color>
               > Blade;
  return StylePtr<Layers<
      InOutHelper<Blade, out_millis, in_millis>,
      TransitionEffectL<TrConcat<TrFade<50>, Black, TrFade<2500>, BatteryLevelStyle, TrDelay<1000>, BatteryLevelStyle, TrFade<1000>>, EFFECT_BOOT>
    > >();
#endif  
}

Preset presets[] = {
   { "Style1", "tracks/Style1.wav",
    StylePtr<ObiwanBladeStyle>(),
    StyleSpecialPtr<CYAN, WHITE, 300, 800>(),
    StyleNormalPtr<CYAN, WHITE, 300, 800>(), "Style1"},
   { "Style2", "tracks/Style2.wav",
    StylePtr<ObiwanBladeStyle>(),
    StyleNormalPtr<CYAN, WHITE, 0, 0>(),
    StyleNormalPtr<CYAN, WHITE, 0, 0>(), "Style2"},
   { "Style3", "tracks/Style3.wav",
    StylePtr<RainbowFireBladeStyle>(),
    StyleRainbowPtr<300, 800>(),
    StyleRainbowPtr<300, 800>(), "Style3"},
   { "Style4", "tracks/Style4.wav",
    StyleNormalPtr<Gradient<Blue,Green,Yellow,Red>, WHITE, 300, 800>(),
    StyleNormalPtr<Gradient<Blue,Green,Yellow,Red>, WHITE, 300, 800>(),
    StyleNormalPtr<Gradient<Blue,Green,Yellow,Red>, WHITE, 300, 800>(), "Style4"},
   { "Flashlight", "tracks/flashlight.wav",
    StylePtr<WHITE>(),
	StylePtr<WHITE>(),
	StylePtr<WHITE>(), "Flashlight"},
   { "Battery", "tracks/battery.wav",
    &style_charging,
    StylePtr<BatteryLevelStyle>(),
    StyleNormalPtr<BLACK, BLACK, 300, 800>(), "Battery\nLevel"}
};
BladeConfig blades[] = {
 { 0,
    WS281XBladePtr<130, bladePin, Color8::GRB, PowerPINS<bladePowerPin2, bladePowerPin3> >(), // main neopixel blade & hilt connector in parallel
    DimBlade(25.0, WS281XBladePtr<6, blade3Pin, Color8::GRB, PowerPINS<bladePowerPin2, bladePowerPin3> >()), // chassis blade connector neopixels
    WS281XBladePtr<2, blade2Pin, Color8::GRB, PowerPINS<bladePowerPin4> >(), // kyber crystal neopixels
    CONFIGARRAY(presets) },
};
#endif

#ifdef CONFIG_BUTTONS
Button PowerButton(BUTTON_POWER, powerButtonPin, "pow");
Button AuxButton(BUTTON_AUX, auxPin, "aux");
#endif

Only the layers below the inout layer should affect if the blade stays on or not.
The BatteryLevelStyle youā€™re using looks reasonable, but testing without it would be prudent.

If you have shared power pins and a pogo pin PCB with LEDs on it, then the main blade style can cause the power for the pogo pin PCB to stay on as well.

Either way, you can try replacing the complicated styles with simple ones and see if makes a difference.

Also, your config file seems to have two blades that use the same power pins, but does not have SHARED_POWER_PINS. Iā€™m not sure it that is causing any issues, but you should probably add it.

Thank you for the input.

I will do some testing with simpler styles once I get my hands on the saber again. Although I am sure I can teach my brother how to update the firmware, he does not have proper measurement equipment to measure the current precisely.

If I understand SHARED_POWER_PINS correctly, then that adds only usage counters for the LED pins, so that the first turned off blade on the pins does not turn off the pins as long as there is something else using them. Thus, leaving it undefined only could add a tendency to switch things off too early, resulting in less power consumption. I will try adding it anyways.

You are right about SHARED_POWER_PINS. But itā€™s definitely worth giving it a try just in case there is something weird going on with it. ProffieOS doesnā€™t just call digitalWrite(0) when turning the pin off, it also disables the pin. Itā€™s possible (but unlikely) that something weird happens when trying to disable a pin twice.