Boron falsely claims LiPo is charging and eternally discharges LiPo despite plugged-in USB and Yellow charging light on

Newest version, 3.0.0-rc1:

This is highly infuriating. I designed my PCBs to make the other boards run-off Boron LiPo and Boron is supposed to charge the LiPo through VUSB. I’ve been with Particle for 1.75 years and I still haven’t seen simple, working implementation of Solar charging supported in newest firmware versions where there isn’t some critical issue (once the Boron-never-reconnect issue was fixed, the red-SOS-crash-on-basic-Serial-RX catastrophic failure was introduce, and this was just fixed in V3.0.0-rc1).

But alas, forget about solar charging, now Boron won’t even regularly charge LiPo when connected over USB 5V, which always worked before.

It does not charge and I have no code disabling charging. It also seems not to charge still even after I call the following code:
PMIC pmic;
pmic.begin();
pmic.setInputVoltageLimit(4840);
pmic.setInputCurrentLimit(900) ;
pmic.setChargeVoltage(4208);
pmic.setChargeCurrent(0,0,1,0,0,0);
pmic.enableDPDM();
pmic.enableBuck();
pmic.enableCharging();

Does Particle publish a Device OS version for the Boron, which, all at the same time:

  1. Won’t infinitely disconnect and never reconnect due to incorrect memory handling coding practices in the firmware;
  2. Won’t randomly hard crash because the UART RX received some data; and
  3. Is capable of charging the LiPo battery?

Thank you for your help in advance.

Hi Paul,

Since DeviceOS 1.5.x the new Power Manager takes care of charging. I suspect your PMIC calls are being ignored.
https://docs.particle.io/reference/device-os/firmware/boron/#power-manager
For PMIC calls to be respected you need to lock the thread by calling PMIC power(true); I do however suggest that you start using the Power Manager.

Can you run the following code to verify what settings are currently set:

        PMIC power(true);
        Log.trace("Current PMIC settings:");
        Log.trace("VIN Vmin: %u", power.getInputVoltageLimit());
        Log.trace("VIN Imax: %u", power.getInputCurrentLimit());
        Log.trace("Ichg: %u", power.getChargeCurrentValue());
        Log.trace("Iterm: %u", power.getChargeVoltageValue());

        int powerSource = System.powerSource();
        int batteryState = System.batteryState();
        float batterySoc = System.batteryCharge();
        
        constexpr char const* batteryStates[] = {
            "unknown", "not charging", "charging",
            "charged", "discharging", "fault", "disconnected"
        };
        constexpr char const* powerSources[] = {
            "unknown", "vin", "usb host", "usb adapter",
            "usb otg", "battery"
        };

        Log.trace("Power source: %s", powerSources[std::max(0, powerSource)]);
        Log.trace("Battery state: %s", batteryStates[std::max(0, batteryState)]);
        Log.trace("Battery charge: %f", batterySoc);
2 Likes

@no1089 Chris, for all the problems I’ve had with Particle Boron (many now totally fixed), the human component and customer service has always been top-notch.

I will do this and get back to you.

I recommend more visible documentation and fixing the default status of no-charge.

Thank you.

@Paul_M :slight_smile: Thanks for the feedback.
Please do.

I’m in the middle of a Power management article that will cover all of the above and more. There are many parts to tie together, so it’s taking a while. Hang tight!

2 Likes

Here are the results of your diagnostic. Had to particle.publish-ify these since I’m not connected to it over my computer USB:
image

Due to the difficulties of Particle.publish accepting string variables, I am having to edit your code to get the last three values. Will follow up in a moment.

@no1089 Power source = 1 (“vin”, it seems); Battery state = 2 (“charging”)
The cloud interface also falsely reports that is charging.
The system power manager thinks it is charging the LiPo when it is not.

This is definitely an odd scenario. I suspect it is charging, but at an insufficient rate.
Your charge rate is double what the source is capable of. (VIN Imax)

I’d suggest doing the following:

  • Don’t use your PMIC setting for the moment, but focus on the Power Manager:

To that end, this will clear the settings:

SystemPowerConfiguration conf;
System.setPowerConfiguration(SystemPowerConfiguration());

Once that’s done - it should charge on a blank/blink sketch.

Afterwards I’d suggest running the following:

void setup() {
    SystemPowerConfiguration conf;
    conf.powerSourceMaxCurrent(550) // Set maximum current the power source can provide (applies only when powered through VIN)
        .powerSourceMinVoltage(4300) // Set minimum voltage the power source can provide (applies only when powered through VIN)
        .batteryChargeCurrent(550) // Set battery charge current
        .batteryChargeVoltage(4210) // Set battery termination voltage
        .feature(SystemPowerFeature::USE_VIN_SETTINGS_WITH_USB_HOST) // For the cases where the device is powered through VIN
                                                                     // but the USB cable is connected to a USB host, this feature flag
                                                                     // enforces the voltage/current limits specified in the configuration
                                                                     // (where by default the device would be thinking that it's powered by the USB Host)
    int res = System.setPowerConfiguration(conf); // returns SYSTEM_ERROR_NONE (0) in case of success

    // Settings are persisted.
}

void loop() {
    {
        PMIC power(true);
        Log.trace("Current PMIC settings:");
        Log.trace("VIN Vmin: %u", power.getInputVoltageLimit());
        Log.trace("VIN Imax: %u", power.getInputCurrentLimit());
        Log.trace("Ichg: %u", power.getChargeCurrentValue());
        Log.trace("Iterm: %u", power.getChargeVoltageValue());

        int powerSource = System.powerSource();
        int batteryState = System.batteryState();
        float batterySoc = System.batteryCharge();
        
        constexpr char const* batteryStates[] = {
            "unknown", "not charging", "charging",
            "charged", "discharging", "fault", "disconnected"
        };
        constexpr char const* powerSources[] = {
            "unknown", "vin", "usb host", "usb adapter",
            "usb otg", "battery"
        };

        Log.trace("Power source: %s", powerSources[std::max(0, powerSource)]);
        Log.trace("Battery state: %s", batteryStates[std::max(0, batteryState)]);
        Log.trace("Battery charge: %f", batterySoc);
    }
    delay(1000);
}

I have not tried the second half, but I do confirm that your two-line reset code did restore the default behavior and instantly started charging the LiPo.

So there are things to be fixed in terms of the system incorrectly reporting charging, with even the yellow charge light on, when there is some surprising and unanticipated persistence of settings that need to be manually corrected with your code for defined behavior.

But this frustration aside, your code has given me a way to make it work, and now I am excited to see if I can dynamically enable/disable charging (which would be based off temperature in the real world case) to see if solar charging is finally stably implementable.

I do hope that a distinction between VIn and VUsb does not come into play with this next experiment. For eventual solar I need to charge with VIn and not have some unprofessional moveable USB connector hack.

Thanks for providing a workaround.

I am successfully solar charging with a 6V Voltaic panel hooked up to VIN. I also successfully enable/disable charging based on temperature in V2.0.1

//Enable/disable charging based on the temperature                                                        
    if ((TempF < 340 || TempF > 900) && chargeEnabled) {     
      PMIC pmic(true); 
      pmic.disableCharging();
      chargeEnabled = false;
      Log.info("Battery charging is diabled due to unsafe temperature");                                           
    }
    else if ((TempF > 340 && TempF < 900) && !chargeEnabled) {
      PMIC pmic(true); 
      pmic.enableCharging();
      chargeEnabled = true;
      Log.info("Battery charging is enabled due to safe temperature"); 
    }

I also can dynamically change the PMIC settings via the cloud configuration so a user can flip between USB charging or Solar charging. It also defaults back to USB settings when the battery gets low so it can always be charged via USB when the battery is low or the system is first powered on. This is explained in detail here: Boron Solar Charging with 1.5.0-rc1 - #60 by jgskarda

What you are asking the Boron to do is certainly capable… just take the time to code it and properly debug it rather than getting frustrated so quickly. Honestly, in the world of programming… 80% of the time is debugging. In the ideal world 80% of the time would be coding and it would just work… but in practice, it’s more like 20% of the time is writing code the remaining 80% is debugging and learning. At least that is what it is for me. It’s just part of the fun. :slight_smile:

1 Like

@jgskarda Thanks Jeff for sharing this. The background information to my frustration is I have, multiple times, had to drive 3 hours both ways to manually recover Boron devices because legitimate firmware bugs used to make PMIC calls crash Boron into a state of irrecoverability. On top of the lasting bug I noted above (though not catastrophic) and the confusion with the new PowerManagement API over PMIC, on top of the additional flurry of info when we needed to call some sort of interrupt detach at setup() to prevent PMIC from crashing, and given the absence of the in-progress power note, I simply abandoned this topic until sufficient improvements were made.

It does look like those improvements have been made in newer firmware versions, and now that 3.0.0-rc1 fixes the issue I reported of Boron randomly red-SOS hard crashing on Serial RX in, it seems the product now finally might be very valuable and usable to me.

The ability to use LiPo for permanent year-round charging eliminates the need for heavy SLA battery setups at my remote sites.

Thank you for sharing your experience and it seems the “(true)” parameter on PMIC is what I was missing.

1 Like

Does the LTS 2.0.1 includes the fixe? I too had a lot of issues because I’m still using PMIC and need to transition.

Hi Paul,

Have you had a chance to test the new features?

Once you’ve set the charging parameters and if you use .feature(SystemPowerFeature::USE_VIN_SETTINGS_WITH_USB_HOST) the device will ignore the USB negotiated charging stats and charge just like if it was plugged into VUSB(VIN).