Electron turns on, but shuts off after 1 second

Hi,

My electrons in the field turn on for about 2 seconds, but then shut off before it even starts to try to connect. Any ideas what is causing this?

I have 5 Volt batteries connected into the Li+ pin. And I have about 50 devices out in the field experiencing a range of temperatures from 32-120 degrees Fahrenheit. They’ve been running for about 2 months and haven’t had problems til now.

Additionally there are magnets as part of the casing, maybe that’s affecting it, but I’m not sure what is causing this. Any ideas?

Thanks!

@liddlem, it sounds like the Electron is not getting enough current. What RGB LED activity are you seeing on the device?

When you say “shuts off”, do you mean it goes to sleep or simply continues the on/off cycle?

I see a breathing white light and then the light shuts off. I assume it reverts back into deep sleep, but maybe it’s off all the way. Not sure.

Additionally some of the devices will eventually turn on for the whole cycle if we press reset a few times.

@liddlem, did you measure the voltage at Li++ and 3V3 for the failing unit? Does your code call for sleep which could be causing the issue?

I suspect the battery voltage may be low and/or it can no longer supply the necessary current.

The voltage on our 3 lithium AA batteries is at 5.1 V.

The device does go to deep sleep after connecting and then wakes up after a certain amount of time. I didn’t check the 3V3 pin but now the specific device we are testing with turns on just fine now and we can’t reproduce the problem with that one. I’ll check with another device.

That 5.1 is on Li++.

When it’s sleeping the Li++ is at 5 V and 3V3 is at 3.3 V

@liddlem, figures that it would be working fine now! Are humidity or ambient temperature possible culprits?

Humidity is not an issue, but ambient temperature can range from 32-120 Fahrenheit is what it has experienced so far.

I have no idea what’s wrong, but I need it to work reliably

@peekay123 Yeah it’s funny how things start working. So I did a hard reset for my devices. Some worked, but I could always get it working properly if I removed power completely, and then held down the reset button for 5 seconds. I did it about 10 times and it worked every time to get things working properly.

Could it be something wrong in the code or timer?

I’m at a total loss, and I can’t afford to have that happen.

@liddlem can you post your code?

indent preformatted text by 4 spaces     */
/*----------------------------------------------------------------------------------------------------------------------
 *                                      INCLUDES
 */
#include "Particle.h"
#include "cellular_hal.h"
#include <math.h>
#include <string.h>

/*----------------------------------------------------------------------------------------------------------------------
 *                                      CONSTANTS
 */
#define EVENT_NAME                  "M"
#define NUM_MEAS                    100
//#define MEASURMENT_THRESHOLD        45

#define NUM_QUART                   NUM_MEAS / 4
#define SENSOR_TYPE                 1 // 1 = short sensor; 2 = tall sensor

#define BUFFER_SIZE                 256

#define PIN_SENSOR                  A2
#define PIN_RELAY                   D3

#define CELL_APN                    "hologram"
#define CELL_USERNAME               ""
#define CELL_PASSWORD               ""

#define EEPROM_TIME_ADDRESS         0

#define ELECTRON_PRODUCT_ID         ----
#define ELECTRON_PRODUCT_VERSION    ----

#define DELAY_SETTLE                100          // Milliseconds
#define DELAY_CLOUD                 20000         // Milliseconds
#define DELAY_CONNECT_TIME          500          // Milliseconds
#define UPDATE_TIME                 300000       // Milliseconds
#define DELAY_MEASUREMENT           20           // Milliseconds
#define NOT_CONNECTED_TIMEOUT       120000        // Milliseconds
#define FAILURE_SLEEP_DURATION      3600         // Seconds

/*----------------------------------------------------------------------------------------------------------------------
 *                                      CONFIGURATION
 */
PRODUCT_ID(ELECTRON_PRODUCT_ID); // Product ID
PRODUCT_VERSION(ELECTRON_PRODUCT_VERSION); 
STARTUP(cellular_credentials_set(CELL_APN, CELL_USERNAME, CELL_PASSWORD, NULL)); 

STARTUP(System.enableFeature(FEATURE_RESET_INFO)); // To enable reading why the system was reset.
// SYSTEM_THREAD(ENABLED); // for allowing processes to run in parallel. Default is SYSTEM_THREAD disabled. 
SYSTEM_MODE(SEMI_AUTOMATIC); // Cloud connecting at SEMI_AUTOMATIC can provide power savings.

FuelGauge fuel;

/*----------------------------------------------------------------------------------------------------------------------
 *                                      GLOBALS
 */
int m_min = -1;
int m_q1 = -1;
int m_med = -1;
int m_q3 = -1;
int m_max = -1;
float m_fill = -1; 
float m_soc = 0.0;

char publishString[BUFFER_SIZE];

bool published = false;
//bool connectSuccess = false; // used for connection timeout
bool forceConnect = false;
bool connect_and_send = false;

/*======================================================================================================================
 *                                      PARTICLE MAIN CYCLE ENTRY POINTS
 */
void setup() 
{
    initAll();
    DPRINTLN("+++ setup ====================================");
    takeMeasurements();
    slowClock();
    sprintf(publishString, "{\"m_min\":%d,\"m_q1\":%d,\"m_med\":%d,\"m_q3\":%d,\"m_max\":%d,\"m_fill\":%f,\"m_soc\":%f}", m_min, m_q1, m_med, m_q3, m_max, m_fill, m_soc);
    if(!forceConnect) // go to sleep without reporting
    {
        unsigned long t_now = Time.now();   // seconds since Jan 1 1970 (UTC)
        int Sleep_Duration = getSleepDelay(t_now);
        System.sleep(SLEEP_MODE_SOFTPOWEROFF, Sleep_Duration);
    }
    Particle.connect();
    // connectSuccess = waitFor(Particle.connected, NOT_CONNECTED_TIMEOUT); // System threading needs to be enabled for this to work
    waitUntil(Particle.connected);
}

void loop() 
{
    if (published == false)
    {
        published = Particle.publish(EVENT_NAME, publishString, PRIVATE);
    }
    else
    {
        delay(DELAY_CLOUD);
        Particle.disconnect();
        unsigned long t_now = Time.now();   // seconds since Jan 1 1970 (UTC)
        int Sleep_Duration = getSleepDelay(t_now);
        System.sleep(SLEEP_MODE_SOFTPOWEROFF, Sleep_Duration);
    }
    // }
    // else // connection time is greater than 2 minutes, go to sleep for 1 hour
    // {
    // 
    //     System.sleep(SLEEP_MODE_SOFTPOWEROFF, FAILURE_SLEEP_DURATION);
    // }
 }

/*======================================================================================================================
 *                                          HELPER FUNCTIONS
 */
 ///=====================================================================================================================
/// <summary>This function called to initialize everything that is needed</summary>
///=====================================================================================================================
    inline void initAll()
    {
        pinMode(PIN_RELAY, OUTPUT);
        m_soc = fuel.getSoC();
        if(!Time.isValid())
        {
            forceConnect = true;
        }
        if(Time.hour(Time.now()+2*60) == 2) // 8pm daily report
        {
            forceConnect = true;
        }
    }

///=====================================================================================================================
/// <summary>Helper function used in takeMeasurements to turn on the solid state relay</summary>
///=====================================================================================================================
void turnOnRelay() {
    digitalWrite(PIN_RELAY,HIGH);
    delay(DELAY_SETTLE);
}

///=====================================================================================================================
/// <summary>Helper function used in takeMeasurements to turn off the solid state relay</summary>
///=====================================================================================================================
void turnOffRelay() {
    digitalWrite(PIN_RELAY,LOW);
    delay(DELAY_SETTLE);
}

///=====================================================================================================================
/// <summary>
///  This function slows down the clock from 120MHz to 30MHz in order to be more power efficent.  This function was 
///  written by someone in the particle community (link is on the next line)
///  https://community.particle.io/t/reducing-photon-run-time-power-consumption-howto/18613
/// </summary>
///=====================================================================================================================
void slowClock() {
    RCC->CFGR &= ~0xfcf0;
    RCC->CFGR |= 0x0090;

    SystemCoreClockUpdate();
    SysTick_Configuration();

    FLASH->ACR &= ~FLASH_ACR_PRFTEN;
}

///=====================================================================================================================
/// <summary>
///  Helper function for takeMeasurements that sorts an array of length MEASUREMENT_COUNT using the insertion sort 
///  algorithm
/// </summary>
/// <param name="myReadings">
///  the array of integers that are being sorted (no need to pass by reference)
/// </param>
///=====================================================================================================================
void sortArray(int myReadings[])
{
    int i, j, x;
    for(i = 1; i < NUM_MEAS; i++)
    {
        x = myReadings[i];
        j = i - 1;
        while(j >= 0 && (myReadings[j] > x))
        {
            myReadings[j + 1] = myReadings[j];
            j = j - 1;
        }
        myReadings[j + 1] = x;
    }
}

///=====================================================================================================================
/// <summary>
///  This function takes measurements from an IR sensor and stores the results in 5
///  global variables so that the results are ready to publish
/// </summary>
///=====================================================================================================================
void takeMeasurements()
{
    turnOnRelay();
    int rawMeasurements[NUM_MEAS];
    for(int i = 0; i < NUM_MEAS; i++)
    {
        rawMeasurements[i] = analogRead(PIN_SENSOR);
        delay(20);
    }
    turnOffRelay();
    sortArray(rawMeasurements);
    m_min = rawMeasurements[0];
    m_q1 =  rawMeasurements[NUM_QUART - 1];
    m_med = rawMeasurements[(2 * NUM_QUART) - 1];
    m_q3 =  rawMeasurements[(3 * NUM_QUART) - 1];
    m_max = rawMeasurements[NUM_MEAS - 1];
    if (m_q1 < 515) {
        m_fill = 0.00;
    }
    else if (m_q1 < 625) {
        m_fill = .25;
    }
    else if (m_q1 < 650) {
        m_fill = .50;
    }
    else if (m_q1 < 885) {
        m_fill = .75;
        connect_and_send = true;
    }
    else {
        m_fill = 1.00;
        connect_and_send = true;
    }
    
    if(connect_and_send == true) // threshold reached
    {
        forceConnect = true;
    }
}

///=====================================================================================================================
/// <summary>
///  This function uses the time of day to determine how long to sleep until another measurement is taken.  Currently it 
///  has been written to operate differently in 3 different time ranges:
///  8am - 5pm MDT : go to sleep until the next hour
///  6pm - 7pm MDT : go to sleep until 8pm MDT
///  8pm - 7am MDT : go to sleep until 8am MDT
/// </summary>
/// <returns>Seconds until wakeup</returns>
///=====================================================================================================================
int getSleepDelay(unsigned long t_unix) 
{
    int t_hour = Time.hour(t_unix);          // hour (0-23) in UTC
    int r_sec;
    if(isInRange(t_hour, 14, 23)) // (14 - 23 UTC) or (8am - 5pm MDT)
    {
        r_sec = secUntilNextHour(t_unix);
    }
    else if(isInRange(t_hour, 0, 1)) // (0 - 1 UTC) or (6pm - 7pm MDT)
    {
        r_sec = secUntilNextHour(t_unix);
        int r_hours = hoursUntilStart(t_hour, 2); // (2 UTC) or (8pm MDT)
        r_sec += (r_hours - 1) * 3600;
    }
    else // (2 - 13) UTC or (8pm - 7am)
    {
        r_sec = secUntilNextHour(t_unix);
        int r_hours = hoursUntilStart(t_hour, 14); // (14 UTC) or (8am MDT)
        r_sec += (r_hours - 1) * 3600;
    }
    return r_sec;
}

///=====================================================================================================================
/// <summary>
///  Helper function for getSleepDelay used to determine how many seconds are remaining until the beginning of the next 
///  hour.
/// </summary>
/// <param name="t_unix">unix time that holds how many seconds have elapsed since Jan 1 1970 in UTC timezone</param>
/// <returns>Number of seconds until the beginning of the next hour</returns>
///=====================================================================================================================
int secUntilNextHour(unsigned long t_unix) 
{
    int t_minute = Time.minute(t_unix);
    int t_second = Time.second(t_unix);
    int r_minute = 59 - t_minute;
    int r_second = 59 - t_second;
    return r_minute * 60 + r_second;
}

///=====================================================================================================================
/// <summary>
///  Helper function for getSleepDelay that is used to determin hour many hours need to elapse until the target hour has
///  been reached.
/// </summary>
/// <param name="hour_now">The current hour - expected to be within the range (0 - 23)</param>
/// <param name="hour_target">The target hour - expected to be within the range (0 - 23)</param>
/// <returns>The number of hours needed to elapse until the target hour will be reached</returns>
///=====================================================================================================================
int hoursUntilStart(int hour_now, int hour_target)
{
    return (hour_target + 24 - hour_now) % 24;
}

///=====================================================================================================================
/// <summary>
///  Helper function for getSleepDelay that is used to determine if the current hour is within a specified range.
/// </summary>
/// <param name="hour_now">The current hour - expected to be within the range (0 - 23)</param>
/// <param name="lower">The lower hour boundary</param>
/// <param name="upper">The upper hour boundary</param>
/// <returns>True if within range, false if outside of range</returns>
///=====================================================================================================================
bool isInRange(int hour_now, int lower, int upper)
{
    if(lower < upper)
    {
        return (hour_now >= lower) && (hour_now <= upper);
    }
    return (hour_now >= lower) || (hour_now <= upper); // in case the range wraps around the 23 - 0 hour boundary
}

I’ve only seen this with a low battery voltage. It tries to connect but never does or goes all white.

It could be your batteries voltage dropping out and not handling the current at the lower temps but you would need to test that or switch to a fully charged Li-Po under the same conditions to see if it happens with that setup also. The Li-Po worked for me outside in the winter without problems with temps below 32F.

Li-Po’s are good to discharge at -20C but are not recommended to charge below 0C.

I charged at below negative 0C without any short-term problems but was charging at 100-300mA max from a solar panel. I relied on the PMIC protections to keep it from charging at below 0C but I’m not sure if that protection is actually enabled on the Electron or not.

Since your problem went away now, could it have been low temps when it happened?

You could put that setup in the freezer for a day and see if you can get it to happen again.

Yeah… I can’t reproduce the error anymore… I tried putting it in the freezer. If it’s an issue, I’m sure it will come up again. Maybe it’s something in the code…

@liddlem, I always look for buffer overruns so you may want to user snprintf() instead of sprintf() to protect against that. Reducing the CPU clock on the Electron may or may not be an issue. I noticed in loop() that if a publish fails, another publish will be attempted when loop() runs again which is basically very fast. You may want to consider a better exception handler which could attempt several publishes at one second intervals and if that fails, recycle the modem power or queue the publish data. Any reason you disabled SYSTEM_THREAD()?

1 Like

@peekay123, For the sprintf() I have the buffer set as 256 in the global variables section. Do I still need to switch this to snprintf() to avoid buffer overruns?

I agree with what you’re saying about the loop() function, and also it’s possible the CPU clock could be causing some weird things since it’s not running as usual.

The reason for disabling SYSTEM_THREAD() is that there is not much useful functionality for my application and when having the SYSTEM_THREAD(ENABLED) then it would take a really long time of it being connected to the cloud to do OTA updates. For my application I want things running serially and nothing running in the background that I don’t control when it happens.

snprintf() is always the safer bet.
And over-dimensioning buffers just to avoid the use of it won’t protect you but just make crashes less frequent and hence harder to debug.

1 Like

A simple way to rule out the code, is to put a bare bones stock app in it. Let that run for a long while and see if it acts up.
If not then you know its something with the code.

Sounds great! Thanks to @ScruffR and @seulater.

I’ll try that out and see. @seulater do you have any ideas on how to identify what’s wrong in the code? I can’t seem to find anything wrong with it. Maybe the snprintf() would fix it, but not sure.

When I run into issues like this, my method is to start slapping in a but-load of Serial.printf’s everywhere.
This way when it hangs, locks up, or misbehaves I can see in the terminal the last thing it did. Typically once you know what is causing the problem its a easy fix.

3 Likes

@peekay123

Since the Li+ pin has 5.1 and the data sheet recommends 3.7-4.6 V is that an issue that is perhaps causing this? I looked carefully at the PMIC datasheet, and it supposedly takes up to 6V as absolute max ratings, but I’m curious if this might be an issue.