Issue when USB is disconnected

In my loop there is code to detect the source of power (DiagnosticsHelperRK) and to use different methods for sleep/delay depending on whether we’re being powered by USB or battery.

The issue that I have can be described as being “bricked” in a solid cyan. The scenario leading to this is:

  1. running on battery and in a sleep (10 mins)
  2. plug in USB (loop code stays asleep)
  3. wake up at normal time and loop resumes
  4. code does its thing and publishes to Particle, then because we are connected to USB calls delay() instead of sleep.
  5. during delay (10 mins) manually disconnect USB (loop stays in delay, cyan is breathing normally)
  6. delay elapses at normal time, cyan goes solid.
    Boron is stuck at this point.
  7. press reset to recover

Maybe there is a handler to detect the disconnect, or something else that my code can do to tolerate the USB disconnect during delay so that upon exiting of the delay we are not stuck?

If you are using delay(600000); then the Boron isn’t allowed to perform any other actions, like Service the Cloud Connection, etc. That’s a general statement, not an absolute one.

Using SYSTEM_THREAD(ENABLED) and Something like this might be a better choice (assuming you’re using delay) :

  for (uint32_t ms = millis(); millis() - ms < 600000; Particle.process()); // instead of 10-minute delay()
1 Like

Yes, on battery we turn cellular off and sleep. Power consumption is very low. On USB we can be less concerned about power so we delay. I’ll try your approach, thanks! BTW, I wasn’t sure of “solid cyan” meant something.

 int pcnt;
 Log.info("Delay start for USB (2 minutes) at %s", (const char*)Time.timeStr());\
 for (uint32_t ms = millis(); millis() - ms < 120000; Particle.process()) { pcnt++; } // instead of delay()
 // delay(120000); 
 Log.info("Delay end for USB with %d iterations at %s", pcnt, (const char*)Time.timeStr());

Yields:
0000154355 [app] INFO: Delay start for USB (2 minutes) at Tue Mar 12 18:36:05 2019
0000274356 [app] INFO: Delay end for USB with 4430123 iterations at Tue Mar 12 18:38:05 2019

Is the number of calls to Particle.process() captured here in a 2 minute timeframe expected?

Quick update: if I disconnect the USB cable, the Boron still gets “bricked” coming out of the Particle.process() loop.

Can you post a stripped down version of your code to test?

BTW, bricked has a slightly different meaning :wink:
When your device is recoverable, it’s not bricked.

1 Like

Solid colors don’t mean anything other than that the device is locked up.

Appreciate the feedback regarding locked up Boron and related terminology. The lock-up happens by unplugging the USB during the delay. I have a power switch built in so it is easy enough to recycle but that’s not ideal.

I am also happy to share my firmware since so many techniques where adapted from the forum. Note that short of using the Adafruit TPL5111 the code below is demonstrating my lowest in-line sleep power consumption thus far. Suggestions for further improvements related to power reduction (or anything for that matter) are welcome!

#include "Particle.h"
#include <DS18B20.h>
#include <math.h>
#include <DiagnosticsHelperRK.h>

// DS18B20 set up
const int MAXRETRY = 4; // retry limit
DS18B20 ds18b20(D4, true); //Sets Pin D4 for Temp Sensor, D3 Low, D5 High

// LoopDelayDefault constant
const int LOOPDEFAULT = 3540000;  // roughly 1 hour

String  fstr;
String  myID;
String  firmware;
String  vardata;
String  sensordata;
double  celsius;
double  fahrenheit;
int     loopcount;
bool    isChargingEnabled;
int     delaymode;
int     loopdelay;   

// Function prototypes  
// Could be Particle functions
int     chargeStatus(String arg1);
int     powerSource(String arg1);
int     chargEnabled(String arg1);

// Run application on its own thread
SYSTEM_THREAD(ENABLED);
// System setup
SYSTEM_MODE(SEMI_AUTOMATIC);

// Logging
SerialLogHandler logHandler(LOG_LEVEL_ALL);

// Disable battery charging    
PMIC power;

// Battery
FuelGauge fuel;

// Cellular signal;
CellularSignal sig;

// Visual indicator that we are publishing
const int BLUELED = D7; 

/*******************************************************************************
 * Function Name  : setup
 * Description    : None.
 *******************************************************************************/
void setup() {
    
    // Initialize vars
    fstr = "";
    myID = "";
    firmware = "CloudAppV104";
    sensordata = "";
    vardata = "";
    celsius;
    fahrenheit;
    isChargingEnabled = true;
    delaymode = 0;
    loopdelay = LOOPDEFAULT;  
    
    EEPROM.get(10, loopcount);
    if(loopcount == 0xFFFFFFFF) {
        // EEPROM was empty -> initialize value
        loopcount = 0;
    }
   
    // Delay for serial
    Serial.begin();
    delay(5000);
  
    // Set voltage target objective
    power.setChargeVoltage(4208);  // default was 4112
    
    // Set needed pins
    pinMode(BLUELED, OUTPUT);
    pinMode(D3, OUTPUT);
    pinMode(D5, OUTPUT);
    
    // Onboard sensor (not configured as an array or bus)
    digitalWrite(D3, LOW);   // ground
    digitalWrite(D5, HIGH);  // voltage
    
    // Connect to the cloud if not already
    if (!Particle.connected()) {
        Particle.connect(); 
        delay(15000);
    }
    
    // Log Messages
    Log.info("System version: %s", (const char*)System.version());
    Log.info("System deviceID: %s", (const char*)System.deviceID());
    
    // End of setup
    Log.info("Setup end");
}


/*******************************************************************************
 * Function Name  : loop
 * Description    : None.
 *******************************************************************************/
void loop() {
    // Loop start
    Log.info("Loop start");
  
    // Are we connected?
    if (Particle.connected()) {
        Log.info("Already connected to the cloud");
    } else {
        Log.info("Connecting to the cloud");
        Particle.connect();
        delay(15000);
    }
   
    // Cellular signal
    sig = Cellular.RSSI();
    
    // Loopcount
    loopcount++;
    // Signed int - lets not overflow it
    if (loopcount == 2147483647) loopcount = 0;
    EEPROM.put(10, loopcount);
    
    // Get the temperatures
    getTemp();
    
    // Adjust for testing
    fahrenheit = fahrenheit + 0;
    
    // Format the temperature
    fstr = String::format("%04d",int(fahrenheit));
    
    // If USB is connected then there is no point in disabling cellular, just
    // use simple delay mechanism
    if (powerSource("x") < 5) 
    {
        if (delaymode==0) {
            // if we were on delaymode 0 (battery) but are
            // now plugged in, change to 1 (USB).
            delaymode=1;
        }
    }
    // If we are back on battery then flip back 
    // to the most efficient mode.
    else delaymode = 0;
    
    // Allow battery charging?
    if ((celsius < 0) || (powerSource("x") > 4)) {
        power.disableCharging();
        isChargingEnabled=false;
    } else {
        power.enableCharging();
        isChargingEnabled=true;
    }
    
    // START PUBLISHING
    // START PUBLISHING
    digitalWrite(BLUELED, HIGH);
    vardata = String::format("F=%s L=%d D=%d M=%d P=%d E=%d C=%d",(const char*)firmware,loopcount,loopdelay,delaymode,powerSource("x"),isChargingEnabled,chargeStatus("x"));
    sensordata = String::format("V1|%2.2f|%f|V=%f|S=%.02f%%|Q=%.02f%%|%s",celsius,fuel.getSoC(),fuel.getVCell(),sig.getStrength(),sig.getQuality(),(const char*)vardata);
    
    // Trigger the webhook
    if (Particle.connected()) {
        Particle.publish("sensordata", sensordata, PRIVATE, NO_ACK);
        // Write it to the log
        Log.info(sensordata);
    }
    
    delay(5000);
    digitalWrite(BLUELED, LOW);
    // END PUBLISHING
    // END PUBLISHING
    
    // For testing
    // delaymode = 0;
    
    // delaymode 1 is the default when USB
    // is connected.  delaymode 0 when running on battery.
    if (delaymode==1) { 
        // USB
        Log.info("Delay (2 minutes) at %d past the hour", Time.minute());
        delay(115000);
    } else {
        // Battery (delaymode 0)
        // 02mAh with no pulse - BSF WITHOUT TPL5111
        // ~1%/day on 6600mAh Li-ion battery
        // how can we get this down even lower??
        Log.info("Sleep with %d seconds at %d past the hour", (loopdelay/1000), Time.minute());
        Cellular.off();
        delay(5000);
        System.sleep({}, {}, (loopdelay/1000)); 
        Cellular.on();
        Cellular.connect();
        delay(5000);
    }
    
    // Loop end
    Log.info("Loop end");
}


/*******************************************************************************
 * Function Name  : getTemp
 * Description    : None.
 *******************************************************************************/
void getTemp() {
    // Only fire up the probe when needed
    digitalWrite(D5, HIGH);  
    delay(1000);
    float _temp;
    int   i = 0;
    
    do { _temp = ds18b20.getTemperature(); } 
    while (!ds18b20.crcCheck() && MAXRETRY > i++);
    if (i < MAXRETRY) {
        celsius = _temp;
        fahrenheit = ds18b20.convertToFahrenheit(_temp);
    } else {
        celsius = fahrenheit = -99;
    }
    // Turn the probe off
    digitalWrite(D5, LOW);  // voltage
}


/*******************************************************************************
 * Function Name  : chargeStatus
 * Description    : is battery charging
 *******************************************************************************/
int chargeStatus(String arg1) {
    Log.info("Charging status requested: %d", (((power.getSystemStatus() >> 4) & 0x3)==2));
    return (((power.getSystemStatus() >> 4) & 0x3)==2);
}


/*******************************************************************************
 * Function Name  : powerSource
 * Description    : what is our power source?
 *******************************************************************************/
int powerSource(String arg1) {
    // POWER_SOURCE_UNKNOWN = 0
	// POWER_SOURCE_VIN = 1
	// POWER_SOURCE_USB_HOST = 2
	// POWER_SOURCE_USB_ADAPTER = 3
	// POWER_SOURCE_USB_OTG = 4
	// POWER_SOURCE_BATTERY = 5
    int pwr = DiagnosticsHelper::getValue(DIAG_ID_SYSTEM_POWER_SOURCE);
    Log.info("Power source is: %d", pwr);
    return pwr;
}


/*******************************************************************************
 * Function Name  : chargEnabled
 * Description    : Is charger enabled
 *******************************************************************************/
int chargEnabled(String arg1) {
    Log.info("Is charging enabled requested: %d", isChargingEnabled);
    return (int) isChargingEnabled;
}

Just to make sure, you haven’t got any external circuitry powered off the VUSB pin (which will of course be without power when USB power is gone).

What you are seeing might be in connection with this issue.
When you unplugging USB you are also disconnecting the USB Serial connection which is not handled well

1 Like

Nothing external. What is interesting is that the Boron does “roll over” without an SOS to the battery upon disconnection from USB. The battery current being used at that point is > 0 and fluctuating, so something is running just not my app.