Boron Solar Charging with 1.5.0-rc1

You bring up a good point.
Then when the Li-Po Voltage gets low, automatically prepare the Boron to Mothball itself into Hibernate with PowerSourceMinVoltage(3880) for Solar and 12V sources.

A “Dead” 12V source would still provide a voltage, but no current to allow the PMIC to maintain the backup Li-Po. The Li-Po would drain, then you enter the failsafe Hibernate Mode.

A 5V Install would already have PowerSourceMinVoltage(3880).
Your Code wouldn’t care which of the (3) Power Sources used for each install.
As you said, you can define that with a Webhook… which basically only really needs to change powerSourceMinVoltage. Then monitor the Li-Po as the failsafe.

1 Like


Going this thread late but would agree with everything that @Rftop said.

I use the same panel as you (Voltaic 3.5W 6V) and put a 470uF cap on DC-In. All this works well. I have the same settings as you but left the MaxCurrent at 900 because, as @Rftop points out, the Min voltage is the key setting.

I did want to pick up on something you said about disabling charging when the temperature is too low. I though this functionality would be added to the Power Configuration API. Without it, you still have to run the older PMIC commands as well as the New API. Was I mistaken that this was supposed to get fixed?



1 Like

Thanks for confirming @chipmc - I am not 100% sure if the new Power Configuration API has a disable charging. Searching through the docs it doesn’t appear like it was included in the API yet. In the sample code I used (which I think I copied from a different post of yours), I did confirm it enables/disables charging like it should using pmic.disableCharging() and pmic.enableCharging() based on the temperature reading.

As for reading the voltage input from the PMIC, I did dig through the datasheet of the bq24195. Page 26 lists the available registers and nothing is mentioned as it pertains to input voltage. Oh well, it was worth a shot. It looks like if I wanted to measure that voltage of the 12VDC batter, I’ll have to do use a GPIO pin and knock a 12V DC voltage down to at least 3.3.

So given the information from both @Rftop and confirmed by @chipmc. I think I now have the info I need. Thank you both very much! Always appreciated!

I just need to take some time to go program the webbook to dynamically change .powerSourceMinVoltage(5080) based on cloud configuration and then to Mothball itself into Hibernate mode with setting back to .powerSourceMinVoltage(3880) once below x% battery percent. Right now, I’m thinking of having the default config first initialized during setup() be .powerSourceMinVoltage(3880). The cloud configuration would only be allowed to set it to .powerSourceMinVoltage(5080) IF the current battery percentage is above say 75%. This way, if the battery ever dies when configured as a Solar Panel mode, it can be recharged via standard USB to near full battery before it can be set back to Solar Panel mode. Thanks again! I’ll report back my further learnings and code snippets I used for this hopefully in the next few days. Hoping for a sunny day tomorrow! :slight_smile:


Just a thought on your last paragraph. There may well be times when the charge level will drop significantly below 75% say after a longish cold snap or a significant period of cloud cover (pesky hurricanes). Unless you want to physically “rescue” your devices, you may well want to keep the minimum voltage setting to facilitate solar charging. In my use case, I can’t easily physically visit my sensors so what I have done is introduce a “low battery” state to my finite state machine. In this state, the device uses the “enable” pin to achieve the lowest power usage and it only wakes to periodically check the battery level. With this protection in place, I have not yet had a battery go completely dead in normal operations.

The case you described where a USB charger can charge the device despite the higher minimum voltage setting is enabled by default as long as your charger uses the DPDM pins to signal its intent to charge your device.

I realize that the pmic.disableCharging() function does work as expected but, I was hoping I could make a clean break with pmic once the new power configuration API was fully deployed.



1 Like

@jgskarda a behavior that I have noticed and take advantage of is that even if you set PowerSourceMinVoltage(5080) and have a device go down requiring a physical intervention with USB charging I just throw the device in Safe Mode which will then utilize the default PowerSourceMinVoltage setting allowing me to USB charge.

1 Like
  • Very good to know. That’s a good fallback for sure. Thanks!

yeah agreed. What I’m actually thinking is the entry condition to initially set .powerSourceMinVoltage(5080) is >75% battery charge and solar charge configured from the webhook response. It would stay with that configuration until the battery was say less than 5%/near dead, just before entering mothball mode. This way the Solar Panel can use the full battery capacity rather than only 25% of it. Hope that makes sense.

[quote=“chipmc, post:52, topic:54680”]
With this protection in place, I have not yet had a battery go completely dead in normal operations.
[/quote] - Fantastic to hear. Looking forward to trying out this Solar Panel in a long term situation! Would be a bit easier. What’s your wake/sleep duration and how many maH battery do you have? I was running a 6600 mAh battery in a wake every 5 minutes to take readings/fall back asleep and then wake every 20 minutes, take readings, turn on cellular/particle.connect() and publish all of the readings to the cloud then fall back asleep. The power hungry part is connecting to the cloud to publish the data. Was getting 2-5 weeks out of it with this config based on signal strength (i think it takes longer to connect each time with weaker signal thus consuming more power). Would be great to get at least 8 weeks or even better increase the frequency at which it can publishes data. Very curious on what results you’ve found. By the way… I also 3D printed a bracket to mount the 3.5W solar panel and use a adjustable camera bracket from Amazon. This way I can easily clamp it on anything and then adjust it very easily. When I get around to it, I’ll post it to the other post with a picture. Would be curious what your using as a bracket as well for our panel.

Now that I updated my board based on the discussion from your carrier board that mentioned this: I received my boards back and everything seems to work perfectly with that watchdog, sleep and system reset using .EN functionality outlined in the application note. So I think I can also use the .EN PIN if needed for the mothball mode to sleep with very little power consumption. Although I’m hoping I can get by without the complexity of using .EN pin.

1 Like


OK, I understand what you mean by 75% now, makes sense.

Here is a rough outline of my approach - in case it helps or if you or anyone else has a suggestion for improvement. :wink:

  • I use 1800-2000mAH batteries which are good for 4-5 days of normal operations
  • My devices generally get a good 8 hours of deep sleep (“Enable” pin) at night, waking only to pet the watchdog every hour.
  • During the day, they “nap” (stop mode sleep) except to handle the following: 1) hardware interrupt from sensors, 2) hourly connect / report via Webhook to Ubidots, 3) Stay awake for 90 seconds at the top of the hour for updates / interaction. This is called “low power mode” and it is the default behavior.
  • I can take the devices out of “low power mode” to “normal operations” when I want to monitor / interact with the device using the particle variables and functions. The device will go back to “low-power” model automatically after a while or if the battery level is low.
  • Finally, there is a “low battery” mode when the device goes into battery preservation mode - currently set at 20@ charge.

I have also switched over to the new watchdog / clock approach outlined in AN023 - very happy with them so far.



1 Like

Good stuff. Thanks for sharing! A few thoughts/comments/questions:

Is this needed when using the internal watchdog? I thought you need to turn off the external watchdog when in sleep mode per AN023 having a max watchdog of 124 seconds. I.e. use
ab1805.stopWDT(); and ab1805.resumeWDT(); correct?

  • Nice. I might steal this. :slight_smile:

Can you elaborate on this difference. Is low power mode stop mode but with wake on cellular giving you an opportunity to wake it up during that 90 second window? I.e. something like this:

SystemSleepConfiguration config;

Finally, here is what I came up with to mount the solar panel. Works well for me to clamp the bracket to nearly anything and then adjust the solar panel based on time of the year and where the sun is in the sky. It’s nothing more than a 3D printed bracket to attach the solar panel to with a 1/4" 20 threaded inserts bought from McMasterCarr and then a camera bracket that has 1/4" 20 threads on one end and a clamp on the other that I bought from Amazon: :

I am still waiting on a sunny day here in WI to really see what this solar panel can do. Hopefully tomorrow. The setting of .powerSourceMinVoltage(5080) does seem to keep the device in the “charging” state vs flickering back/forth between modes at least. It’s just not making much progress yet on increasing battery percentage due to full cloud cover today. That said, I am currently keeping the device in normal operation mode (no sleep/stop/hibernate) just for this testing vs sleeping/waking like I normally do. So I’m probably consuming whatever power the solar panel can generate on a cloudy day.

Thanks for the dialog and for sharing your project. I love the bracket. I simply use the $4.00 one from Voltaic but yours is more adjustable and looks great.

I see you are 3D printing the bracket. Can you share what filament you are using? I am interested in learning how well these parts will stand up to UV light over a few years.

As for your questions:

You are correct, with the new AB1805, I could simply sleep for the full 8 hours. Here is the thing, when the device is in “Enable” sleep, the only way to get it back is to unplug the DC-In and the LiPO battery which is a pain in my setup. If the device wakes for 90 seconds each our and does not turn on the cellular radio, it does not make much of a difference in the battery life. It would make it easier for me to help park personnel recover the device if needed (“wait till the top of the hour and when you see the white light, press the user button”).

The difference between “low power mode” and “normal operations”. Is two fold:
First, low power mode only connects to Particle at the top of the hour. Normal operations is constantly connected to Particle.
Second, low power mode sleeps (STOP mode) between sensor interrupts while normal operations is awake and in the Idle state between interrupts.
Normal operations allows me to monitor the device in real time and interact with it via the Particle console.

Finally, I have not played with the network interface cellular state but am interested in doing so. My concern is that it will significantly degrade battery life but the ability to poll devices at any time, not just the 90 second communication window, could be a game changer.



1 Like

I am printing in PETG. I’ve been told it should hold up really well in outdoor applications just don’t use PLA. I’m a bit skeptical myself but we will see. I didn’t realize Voltaic one was $4. That will probably be a better option for me for if/when I have a permanent installations. I’m not 100% sure what will happen after a year or two and if it gets a heavy wet snow on it. Just was my quick/easy way to mount it for testing and I already use those camera brackets for something else.

That is very cleaver! I like what you did there. I’m contemplating how to best conserve battery when readings are not needed from the device as well. This is very much dependent upon the current temperature. I.e. when it’s freezing cold outside, it doesn’t need to report very often. But when it’s warmer (above freezing) then it needs to report quite often. I was contemplating having the cloud dynamically change the sleep duration based on the hourly weather forecast in that area or waking up and check to see what the temperature is and if still freezing cold, sleep another hour. etc. then fall back asleep. So far I didn’t consider entering EN sleep mode but maybe something I could consider. Overall, I am sure I am overcompensating for a little sloppiness in minimizing power consumption by having a 6600mAH battery and a solar panel but I might be better off with techniques like this!

Something related I was considering was add an ability to enter “Stay Awake mode”. What I mean by that is the webbook response from a publish event would send back a configuration parameter called StayAwakeFlag. Essentially I could flip this flag to true in my database, the status of this flag is sent back to the device after each publish event. Then each time the device publishes new data, I can tell it to stay awake or not. The device publishes new data, waits a few seconds for that response and then immediately falls back asleep if the flag is still false OR stay awake if the flag is true. My initial thought is have the database only allow that to be true for 1 hour and after an hour it would auto set the flag back to false. It sounds good on paper/in my head at least. :slight_smile: We will see how it actually plays out in the coming weeks as I update the web app, DB and device code to test it out. If the device was still healthy/able to occasionally talk to the cloud, you could then enter this mode yourself without having to bother the park rangers.

1 Like

Well today was finally a nice sunny day! And I’ll just say I am quite impressed with how much I got out of the Voltaic 3.5W 6V. Looks like from about 8:00 AM to ~ 4:00 PM it charged my 6600 mAh battery from ~2% --> ~80% in the 8 hours of sun today (obviously slower during the beginning of day and end of day). This was with the device in “normal” operation mode the entire time and not even in stop/sleep mode. I kept it that way so I could keep taking readings throughout the day to see how it’s doing. I suspect I would of had another 5-8% if the device was sleeping. Overall, I’m very impressed and happy with this!

If I’m thinking about it correctly, say I charged 80% of a 6600 mAh battery I stored away 5280 mAh in ~ an 8 hour period. So on average: 660 mAh battery capacity per hour of charging. I.e. averaged 660 mA charge current into a 3.7 V nominal battery. This is 2.442 average watts from that 3.5 watt peak Solar panel. ~70% efficiency on average throughout the day. Overall, I’m very happy with that! Thanks everyone for sharing the settings and the guidance here. Always appreciated. I’ll still implement the sleep cycles, and dynamically change charging parameters as discussed earlier but I’m quite please with this early test. I was getting nearly 1 week of battery life when keeping the device awake the entire time before a solar panel and roughly 3-4 weeks when using a sleep/wake cycle so Solar panel makes a huge difference! Just 1.5 days of sun during this a time period like this would charge it right back up! This is great!

1 Like

Finally got around to implementing the cloud control of solar charging (along with other configuration parameters). Here was my approach in case anyone has some other suggestion or can use it. There is quite a bit here but figured I’d share for any feedback in my approach and maybe someone can use it as well.

Within the Boron:

Initialize values:

//Configuration parameters: -Initialized here
//After initialization, this should be set via PUB/SUB method on some frequency
int16_t config_mode = 1; //Mode - 1:Cellular All-in-on only, 2:Cellular+Lora Gateway, 3:Remote Lora Node
int16_t config_slpInterval_Min = 5;  //Time between taking sensor readings when powered (sec)
int16_t config_rptTime_Min = 20;  //Time between reporting readings to the cloud when not powered
int16_t config_vitalsTime_Min = 120; //Time between publishing vitals
int16_t config_stayAwake_flag = 1; //Duration of time to stay awake. 1 = forever, 0 = immediatly sleep
int16_t config_powerSourceMaxCurrent = 900; //Set maximum current the power source can provide. The default is 900 mA.
int16_t config_powerSourceMinVoltage = 3880; //Set minimum voltage required for VIN to be used. The default is 3880 (3.88 volts).
int16_t config_batteryChargeCurrent = 896; //Sets the battery charge current.The default is 896 mA.
int16_t config_batteryChargeVoltage = 4112; //Sets the battery charge termination voltage. The default is 4112 (4.112V).
int16_t config_indexNum = 0; //Index number of current configuration (0-255)

#define configPtID_mode "1"
#define configPtID_slpInterval_Min "2"
#define configPtID_rptTime_Min "3"
#define configPtID_vitalsTime_Min "4"
#define configPtID_stayAwake_flag "5"
#define configPtID_powerSourceMaxCurrent "6"
#define configPtID_powerSourceMinVoltage "7"
#define configPtID_batteryChargeCurrent "8"
#define configPtID_batteryChargeVoltage "9"
#define configPtID_indexNum "255"

In setup() load the default upon first poweron (we don’t know if it’s solar or mains power yet):

  //Initalize default power configuration for USB charging
  System.setPowerConfiguration(SystemPowerConfiguration());  // To restore the default configuration

Handle the subscription back from each publish event:

void subscriptionHandler(const char *event, const char *data)
{"New Subscription Event: %s",event);"With Data: %s", data);


  bool newPmicCongig_rx = false;
  int intValue;

  //Update device sleep, wake, vitals, stayawayke configuration
  if (jsonParser.getOuterValueByKey(configPtID_mode, intValue)) config_mode = intValue;
  if (jsonParser.getOuterValueByKey(configPtID_slpInterval_Min, intValue)) config_slpInterval_Min = intValue;
  if (jsonParser.getOuterValueByKey(configPtID_rptTime_Min, intValue)) config_rptTime_Min = intValue;
  if (jsonParser.getOuterValueByKey(configPtID_vitalsTime_Min, intValue)) config_vitalsTime_Min = intValue;
  if (jsonParser.getOuterValueByKey(configPtID_stayAwake_flag, intValue)) config_stayAwake_flag = intValue;

  //Update PMIC Configuration:
  if (jsonParser.getOuterValueByKey(configPtID_powerSourceMaxCurrent, intValue)) {
      if (config_powerSourceMaxCurrent != intValue){
        config_powerSourceMaxCurrent = intValue;
        newPmicCongig_rx = true;
  if (jsonParser.getOuterValueByKey(configPtID_powerSourceMinVoltage, intValue)) {
      if (config_powerSourceMinVoltage != intValue){
        config_powerSourceMinVoltage = intValue;
        newPmicCongig_rx = true;
  if (jsonParser.getOuterValueByKey(configPtID_batteryChargeCurrent, intValue)) {
      if (config_batteryChargeCurrent != intValue){
        config_batteryChargeCurrent = intValue;
        newPmicCongig_rx = true;
  if (jsonParser.getOuterValueByKey(configPtID_batteryChargeVoltage, intValue)) {
    if (config_batteryChargeVoltage != intValue){
      config_batteryChargeVoltage = intValue;
      newPmicCongig_rx = true;

  //Update Configuration index Number so cloud knows if it needs to send a new config
  if (jsonParser.getOuterValueByKey(configPtID_indexNum, intValue)) config_indexNum = intValue;

  //update PMIC Config if needed:
  if(newPmicCongig_rx) {"new PMIC Config Rx - Call function");
  pubSub_ResponseRx = true;

Update battery configuration if it changes. Only allow going to Solar Config if battery source is greater than 70 percent.

void pmicConfig(){"Check PMIC Config");
  PMIC power(true);
  if (power.getInputCurrentLimit() != config_powerSourceMaxCurrent || power.getInputVoltageLimit() != config_powerSourceMinVoltage || power.getChargeCurrentValue() != config_batteryChargeCurrent || power.getChargeVoltageValue() != config_batteryChargeVoltage) 
  {"Current PMIC settings is Different than Config");
    //If power source min voltag is greater than 3880 only allow setting it to a higher value if current battery charge level is greater than 70%. This is to ensure the device is adequatly charged before switching to Solar functionality and no longer chargable via USB. 
    if (System.batteryCharge() > 70 || config_powerSourceMinVoltage <= 4000 )
    {"Battery is greater than 70 Perct or power source min voltage <= 4000 accept new config");
      SystemPowerConfiguration conf;
          .powerSourceMinVoltage(config_powerSourceMinVoltage) //3880 for USB, 5080 for Solar
      int res = System.setPowerConfiguration(conf);"setPowerConfiguration1=%d", res);

  //If power source min voltag is greater than 3880 and we are low on battery charge, then set powerSourceMinVoltage to 3880 to ensure the device is capable of USB charging.
  if(power.getInputVoltageLimit() > 3880 && System.batteryCharge() < 10) 
  {"Battery is less than 10 perc and currently configured for greater than 3.880 volts. Use Defaults");
    int res = System.setPowerConfiguration(SystemPowerConfiguration());  // To restore the default configuration"setPowerConfiguration2=%d", res);

Something I don’t show here is when publishing an event, I also include what the current “config_indexNum”.

Within the Python function that I use to processes the data from the publish event, IF the config_indexNum does not equal the config_indexNum reported by the device, it assembles the JSON response of all configuration parameters to send as a response that is handled by the subscriptionHandler()

    response_msg = {"0":1}

    if "configIndex" in msg['data']:
        if (int(msg['data']['configIndex']) != device.device_settings.config_indexNum):
            response_msg = {

    return func.HttpResponse(json.dumps(response_msg))

(Something not shown here is the webhook configuration within the Particle Console but that’s pretty straightforward).

So now, each time I want to send a new configuration to the device, I just update the values and increment the config_indexNum in SQL. The next time the device publishes, it receives a response with the new configuration and settings are updated.

I’m sure I could clean this up a bit or maybe there are prettier ways to write this but it’s functional for me. :slight_smile: What I like about this, is I an also use the “stayAwake” flag, change the slpInterval, reportInterval, vitalsInterval at anytime and those settings are updated the next time the device wakes up and connects to the cloud.

@chipmc - This might be an alternative to consider rather than having a park ranger having to be at the device at a specific time if they need to wake it up for you. Although it does require more of a personalized backend to process the particle webhooks as well as a SQL (or some other DB). I did still use your approach of waking up the device at a specific Time of Day rather than just sleeping for x minutes. This way I know when to expect new data from the device. I just combined both concepts into one.

1 Like


Thank you for sharing. This is an excellent approach and it solves many issues associated with configuration of a large number of “sleepy” devices.

Thank you for sharing. I think I will use this with the new Particle API event on Ubidots as I don’t currently use a custom back-end.



@chipmc I am happy to help as this community and folks like you have helped me tremendously as well along my learning journey. If you ever want more details regarding my backend setup or examples as you work through anything similar feel free to reach out anytime. I hope to eventually document the entire application and maybe share it via GitHub but for now I’m still busy developing/refining.

I’ll have to do some learning regarding Ubidots as it seems straightforward and provides really nice visualization. Might be a good solution for some simple projects I have in mind as well. I hacked together some Python Flask web app for now and use HighCharts for visualization. I plan on overhauling my “front end” visualization later this summer that might be time to learn more about Ubidots and other options.


ASA holds up really well to UV and environmental conditions. It’s a derivative of ABS. It is pretty finicky to print with though. A heated bed is required and an enclosed printer also helps.

@picsil ,

I am still fairly new to 3D printing so I share this in part to see if others have had the same experience. I have found ASA to be harder to print with particularly due to its warping as it cools. However, I also understand that the vapors given off by ASA are more toxic than PETG and can damage plastic parts on the printer if you are using an enclosure.

I have been able to reduce warping to an acceptable degree by:

  • Recommended nozzle temperature: 260 °C
  • Recommended bed temperature: First layer – 105 °C, all other layers – 110 °C.
  • Slowing the first few layers print speed by 25%
  • Adding a “Brim” to help attach the part to the hotbed

I am looking forward to learning more about 3D printing and making cool enclosures like @phillycheeseman has been doing.



I have not yet printed with ASA or Polycarbonate - I have a roll of each direct from Prusa. Prusament PC blend is supposed to be the bomb - we’ll see.

Our house is under construction and we are renting a house nearby - I didn’t want to start printing with these in this house and it is too cold out to do it in my garage.

I did built an enclosure following the Lack table instructions:

I bought three Ikea tables for like $15 each in Canada. Printed the parts as per the files – also search for Lack table mods - people have made some cool adaptations of the core design. I even had to custom 3D design a corner bracket that I wasn’t happy with in the original.


  • Do not use an enclosure for PLA. I learned the hard way that an enclosure makes it too hot for PLA and you get ‘heat creep’ and nozzle clogging.
  • I bought a couple of 50mm fans and I plan to circulate the air automatically; I have an Argon monitoring the Prusa Enclosure.
  • There are lots of square to round fan adapters and filter parts on Prusa and Thingiverse
  • The enclosure is very good at keeping the temp even inside and warmer than without. This is good at the high temps of ASA
  • I have noticed more consistent temperature readings from the nozzle since I added a Silicon nozzle sleeve ($10?). It just keeps the nozzle well heated and no runs or buildup.
  • Add a high ‘skirt’ and brim to reduce air movement around the base. Apparently this helps.
  • Design matters. Different designs will be prone to warping I suspect.
  • When all else fails checkout CNC Kitchen or Makers Muze

Send photos…

Keep us posted…

1 Like

I don’t print a huge number of parts, but I haven’t noticed any significant fumes while printing ASA. But my printer is in my shop and when running a long print I tend to watch it through a webcam instead of sitting near it.

ASA is difficult to print, and I’ve had a lot of warping issues. I print at 260C with a bed temperature of 110C. Since I’ve started adding a brim with 0mm clearance I have greatly reduced the warping issues.

1 Like

I asked a friend who prints with ABS swears by hair spray to stick it to the bed. I use as similar product made for 3D printers but hair spray works too!

1 Like

I also use hair spray, on a heated glass bed. It can’t just be any hair spray though. Aqua Net in the purple spray can works the best. And it’s cheap!

1 Like