Help needed - PMIC settings and String variable corruption

I am trying to figure out what happened here but, the values of some of my variables are getting corrupted (or more precisely taking on non-sensical values). I am not sure of the cause and am hoping someone could give me some ideas on how to proceed in solving these issues. Here are the symptoms:

  • The values for resetCount, dailyPersonCount, and Signal are getting corrupted (see screen shot for examples)
  • The Particle will not boot up unless it is reset from DFU mode. Otherwise, the RGB LED blinks once White and then goes out.
  • The battery will not charge beyond 82% which is new behavior

Some potential causes:

  • My code (please see attached) but I have been working on this code for a while and have never seen anything like this
  • Sleep - the Electron sleeps constantly to save power
  • Power - I am powering the Electron with a 3W / 6V Solar panel which has been able to keep things charged. It may be unrelated but, I noticed some of the symptoms for the first time after running the code from @BDub here.
  • Version 0.7.0r3 firmware - I know, it is Beta but I need the fixes in disconnecting and powering down the cellular modem.

So, please take a look. Any ideas on how I can solve this mystery would be appreciated.

Thanks,

Chip

/*
* Project Cellular-Solar-PIR
* Description: Cellular Connected Data Logger for Solar installations
* Author: Chip McClelland
* Date:8 October 2017
*/

// On the solar installation I am not using the hardware watchdog

// Easy place to change global numbers
//These defines let me change the memory map and configuration without hunting through the whole program
#define VERSIONNUMBER 7             // Increment this number each time the memory map is changed
#define WORDSIZE 8                  // For the Word size
#define PAGESIZE 4096               // Memory size in bytes / word size - 256kb FRAM
// First Word - 8 bytes for setting global values
#define DAILYOFFSET 2               // First word of daily counts
#define HOURLYOFFSET 30             // First word of hourly counts (remember we start counts at 1)
#define DAILYCOUNTNUMBER 28         // used in modulo calculations - sets the # of days stored
#define HOURLYCOUNTNUMBER 4064      // used in modulo calculations - sets the # of hours stored - 256k (4096-14-2)
#define VERSIONADDR 0x0             // Memory Locations By Name not Number
#define SENSITIVITYADDR 0x1         // For the 1st Word locations
#define DEBOUNCEADDR 0x2            // One uint8_t for debounce (stored in cSec mult by 10 for mSec)
#define RESETCOUNT 0x3              // This is where we keep track of how often the Electron was reset
#define DAILYPOINTERADDR 0x4        // One uint8_t for daily pointer
#define HOURLYPOINTERADDR 0x5       // Two bytes for hourly pointer
#define CONTROLREGISTER 0x7         // This is the control register for storing the current state - future use
//Second Word - 8 bytes for storing current counts
#define CURRENTHOURLYCOUNTADDR 0x8  // Current Hourly Count - 16 bits
#define CURRENTDAILYCOUNTADDR 0xA   // Current Daily Count - 16 bits
#define CURRENTCOUNTSTIME 0xC       // Time of last count - 32 bits
//These are the hourly and daily offsets that make up the respective words
#define DAILYDATEOFFSET 1           //Offsets for the value in the daily words
#define DAILYCOUNTOFFSET 2          // Count is a 16-bt value
#define DAILYBATTOFFSET 4           // Where the battery charge is stored
#define HOURLYCOUNTOFFSET 4         // Offsets for the values in the hourly words
#define HOURLYBATTOFFSET 6          // Where the hourly battery charge is stored
// Finally, here are the variables I want to change often and pull them all together here
#define SOFTWARERELEASENUMBER "0.25"
#define PARKCLOSES 20
#define PARKOPENS 7

// Included Libraries
#include "Adafruit_FRAM_I2C.h"                           // Library for FRAM functions
#include "FRAM-Library-Extensions.h"                     // Extends the FRAM Library
#include "electrondoc.h"                                 // Documents pinout

// Prototypes and System Mode calls
SYSTEM_MODE(SEMI_AUTOMATIC);    // This will enable user code to start executing automatically.
SYSTEM_THREAD(ENABLED);         // Means my code will not be held up by Particle processes.
FuelGauge batteryMonitor;       // Prototype for the fuel gauge (included in Particle core library)
PMIC pmic;                      //Initalize the PMIC class so you can call the Power Management functions below.

// State Maching Variables
enum State { INITIALIZATION_STATE, ERROR_STATE, IDLE_STATE, SLEEPING_STATE, NAPPING_STATE, REPORTING_STATE, RESP_WAIT_STATE };
State state = INITIALIZATION_STATE;

// Pin Constants
const int intPin = D3;                      // Acclerometer interrupt pin
const int blueLED = D7;                     // This LED is on the Electron itself
const int userSwitch = D5;                  // User switch with a pull-up resistor
const int tmp36Pin = A0;                    // Simple Analog temperature sensor
const int tmp36Shutdwn = B5;                // Can turn off the TMP-36 to save energy

// Timing Variables
uint32_t publishTimeStamp = 0;         // Keep track of when we publish a webhook
uint32_t webhookWaitTime = 10000;      // How long will we let a webhook go before we give up
uint32_t resetWaitTimeStamp = 0;       // Starts the reset wait clock
uint32_t resetWaitTime = 30000;        // Will wait this lonk before resetting.
uint32_t sleepDelay = 90000;           // Longer delay before sleep when booting up or on the hour - gives time to flash
uint32_t timeTillSleep = 0;            // This will either be short or long depending on nap or sleep
uint32_t napDelay = 3000;              // Normal amount of time after event before taking a nap
uint32_t lastEvent = 0;                // Keeps track of the last time there was an event
bool waiting = false;

// Program Variables
int temperatureF;                    // Global variable so we can monitor via cloud variable
uint8_t resetCount;                      // Counts the number of times the Electron has had a pin reset
bool ledState = LOW;                        // variable used to store the last LED status, to toggle the light
const char* releaseNumber = SOFTWARERELEASENUMBER;  // Displays the release on the menu

// FRAM and Unix time variables
time_t t;
uint8_t lastHour = 0;                   // For recording the startup values
uint8_t lastDate = 0;                   // These values make sure we record events if time has lapsed
uint16_t hourlyPersonCount = 0;           // hourly counter
uint16_t hourlyPersonCountSent = 0;       // Person count in flight to Ubidots
uint16_t dailyPersonCount = 0;            //  daily counter
uint16_t dailyPersonCountSent = 0;        // Daily person count in flight to Ubidots
bool dataInFlight = false;           // Tracks if we have sent data but not yet cleared it from counts until we get confirmation
uint8_t currentHourlyPeriod;            // This is where we will know if the period changed
uint8_t currentDailyPeriod;             // We will keep daily counts as well as period counts

// PIR Sensor variables
volatile bool sensorDetect = false;         // This is the flag that an interrupt is triggered

// Battery monitor
int stateOfCharge = 0;                      // stores battery charge level value

//Menu and Program Variables
uint32_t lastBump = 0;         // set the time of an event
bool inTest = false;             // Are we in a test or not
uint16_t numberHourlyDataPoints;         // How many hourly counts are there
uint16_t numberDailyDataPoints;          // How many daily counts are there
char Signal[17];                            // Used to communicate Wireless RSSI and Description
char* levels[6] = {"Poor", "Low", "Medium", "Good", "Very Good", "Great"};

void setup()
{
  Particle.connect();                         // Connect to Particle on bootup - will disonnect on nap or sleep

  Particle.publish("State","Initalizing");
  pmic.setChargeCurrent(0,0,1,0,0,0);         //Set charging current to 1024mA (512 + 512 offset) thank you @RWB for these two lines
  pmic.setInputCurrentLimit(1500);              //set input current limit to 1.5A
  pmic.setInputVoltageLimit(4840);            //Set the lowest input voltage to 4.84 volts. This keeps my 5v solar panel from operating below 4.84 volts.
  stateOfCharge = int(batteryMonitor.getSoC()); // Percentage of full charge

  pinMode(intPin,INPUT);                      // PIR interrupt pinMode
  pinMode(blueLED, OUTPUT);                   // declare the Blue LED Pin as an output
  pinMode(tmp36Shutdwn,OUTPUT);               // Supports shutting down the TMP-36 to save juice
  digitalWrite(tmp36Shutdwn, HIGH);           // Turns on the temp sensor

  attachInterrupt(intPin,sensorISR,RISING);   // Will know when the PIR sensor is triggered

  char responseTopic[125];
  String deviceID = System.deviceID();
  deviceID.toCharArray(responseTopic,125);
  Particle.subscribe(responseTopic, UbidotsHandler, MY_DEVICES);      // Subscribe to the integration response event

  Particle.variable("HourlyCount", hourlyPersonCount);
  Particle.variable("DailyCount", dailyPersonCount);
  Particle.variable("Signal", Signal);
  Particle.variable("ResetCount", resetCount);
  Particle.variable("Temperature",temperatureF);
  Particle.variable("Release",releaseNumber);
  Particle.variable("stateOfChg", stateOfCharge);

  Particle.function("startStop", startStop);
  Particle.function("resetFRAM", resetFRAM);
  Particle.function("resetCounts",resetCounts);
  Particle.function("Reset",resetNow);
  Particle.function("SendNow",sendNow);

  if (!fram.begin()) {                // you can stick the new i2c addr in here, e.g. begin(0x51);
    Particle.publish("Startup","No FRAM");
    state = ERROR_STATE;
  }

  if (FRAMread8(VERSIONADDR) != VERSIONNUMBER) {  // Check to see if the memory map in the sketch matches the data on the chip
    Particle.publish("Startup","FRAM mismatch - erasing");
    ResetFRAM();                        // Reset the FRAM to correct the issue
    if (FRAMread8(VERSIONADDR) != VERSIONNUMBER) state = ERROR_STATE;  // Resetting did not fix the issue
  }

  resetCount = FRAMread8(RESETCOUNT);         // Retrive system recount data from FRAMwrite8
  if (System.resetReason() == RESET_REASON_PIN_RESET)  // Check to see if we are starting from a pin reset
  {
    resetCount++;
    FRAMwrite8(RESETCOUNT,resetCount);          // If so, store incremented number - watchdog must have done This
  }

  Time.zone(-4);                              // Set time zone to Eastern USA daylight saving time
  getSignalStrength();                        // Test signal strength at startup
  getTemperature();                           // Get Temperature at startup as well
  stateOfCharge = int(batteryMonitor.getSoC()); // Percentage of full charge
  StartStopTest(1);                           // Default action is for the test to be running
  timeTillSleep = sleepDelay;                 // Set initial delay for 60 seconds

  if (stateOfCharge <= 20)  state = SLEEPING_STATE;   // Low battery level will cause an ERROR and reset which brings us here
  else if (state != ERROR_STATE)                      // IDLE unless error from above code
  {
    state = IDLE_STATE;                                // Idle and look for events to change the state
    Particle.publish("State","Idle");
  }
}

void loop()
{
  switch(state) {
  case IDLE_STATE:
    if(hourlyPersonCountSent) {   // Cleared here as there could be counts coming in while "in Flight"
      hourlyPersonCount -= hourlyPersonCountSent;    // Confirmed that count was recevied - clearing
      FRAMwrite16(CURRENTHOURLYCOUNTADDR, hourlyPersonCount);  // Load Hourly Count to memory
      hourlyPersonCountSent = 0;
    }
    if(dailyPersonCountSent) {
      dailyPersonCount -= dailyPersonCountSent;
      FRAMwrite16(CURRENTDAILYCOUNTADDR,dailyPersonCount);
      dailyPersonCountSent = 0;
    }
    if (sensorDetect) recordCount();                                    // The ISR had raised the sensor flag
    if (millis() >= (lastEvent + timeTillSleep)) state = NAPPING_STATE;    // Too long since last sensor flag - time to nap
    if (Time.hour() >= PARKCLOSES) state = SLEEPING_STATE;              // The park is closed, time to sleep
    if (Time.hour() != currentHourlyPeriod) state = REPORTING_STATE;    // We want to report on the hour
    break;

  case SLEEPING_STATE: {
    if (Particle.connected()) {
    Particle.disconnect();                                   // Disconnect from Particle in prep for sleep
    delay(3000);
    Cellular.disconnect();                                   // Disconnect from the cellular network
    delay(3000);
    Cellular.off();                                           // Turn off the cellular modem
    }
    ledState = false;
    digitalWrite(blueLED,LOW);                               // Turn off the LED
    long secondsToOpen = (3600*(24 - Time.hour()+PARKOPENS));  // Set the alarm (in seconds) till park opens again
    System.sleep(SLEEP_MODE_DEEP,secondsToOpen);              // Sleep till morning
    lastEvent = millis();                                    // Reset millis so we don't wake and then nap again
    state = REPORTING_STATE;                                  // Report when we wake from sleep
    } break;

  case NAPPING_STATE: {
    if (Particle.connected())
    {
      Particle.disconnect();                                   // Disconnect from Particle in prep for sleep
      delay(3000);
      Cellular.disconnect();                                   // Disconnect from the cellular network
      delay(3000);
      Cellular.off();                                         // Turn off the cellular modem
      timeTillSleep = napDelay;
    }
    ledState = false;                                        // Turn out the light
    digitalWrite(blueLED,LOW);                               // Turn off the LED
    sensorDetect = true;                                     // Woke up so there must have been an event
    lastEvent = millis();                                    // Reset millis so we don't wake and then nap again
    int secondsToHour = (60*(60 - Time.minute()));          // Time till the top of the hour
    System.sleep(intPin,RISING,secondsToHour);               // Sensor will wake us with an interrupt
    attachInterrupt(intPin,sensorISR,RISING);                // Sensor interrupt from low to high
    state = IDLE_STATE;                                      // Back to the IDLE_STATE after a nap
    } break;

  case REPORTING_STATE: {
    timeTillSleep = sleepDelay;     // Sets the sleep delay to 60 seconds after reporting to give time to flash if needed
    Cellular.on();                                           // turn on the Modem
    Cellular.connect();                                      // Connect to the cellular network
    while(!Cellular.ready())
    Particle.connect();                                      // Connect to Particle
    while(!Particle.connected());
    Particle.process();
    Particle.publish("State","Reporting");         // Temp - for debugging
    if (Time.day() != currentDailyPeriod) {     // Time to log a daily event takes precedence over hourly event
      LogHourlyEvent();
      LogDailyEvent();
      sendEvent(1);
    }
    else if ((Time.hour() != currentHourlyPeriod)) {  // Spring into action each hour on the hour as long as we have counts
      LogHourlyEvent();
      sendEvent(0);
    }
    publishTimeStamp = millis();
    Particle.publish("State","Waiting for Response");
    state = RESP_WAIT_STATE;                         // Wait for Response
    } break;

  case RESP_WAIT_STATE:
    if (!dataInFlight) // Response received
    {
      if (stateOfCharge <= 20) state = ERROR_STATE;     // Very low battery, time to sleep
      else state = IDLE_STATE;                          // Battery OK, proceed
    }
    else if (millis() >= (publishTimeStamp + webhookWaitTime)) state = ERROR_STATE;  // Response timed out
    break;

  case ERROR_STATE:                                          // To be enhanced - where we deal with errors
    if (!waiting)                                            // Will use this flag to wiat 30 seconds before reset
    {
      waiting = true;
      resetWaitTimeStamp = millis();
      Particle.publish("State","Resetting in 30 sec");
    }
    if (millis() >= (resetWaitTimeStamp + resetWaitTime)) System.reset();   // Today, only way out is reset
    break;
  }
}

void recordCount()                                          // Handles counting when the sensor triggers
{
  sensorDetect = false;                                     // Reset the flag
  if (digitalRead(intPin)) {
    Particle.publish("State","Counting");
    lastEvent = millis();                                     // Important to keep from napping too soon
    t = Time.now();
    hourlyPersonCount++;                    // Increment the PersonCount
    FRAMwrite16(CURRENTHOURLYCOUNTADDR, hourlyPersonCount);  // Load Hourly Count to memory
    dailyPersonCount++;                    // Increment the PersonCount
    FRAMwrite16(CURRENTDAILYCOUNTADDR, dailyPersonCount);   // Load Daily Count to memory
    FRAMwrite32(CURRENTCOUNTSTIME, t);   // Write to FRAM - this is so we know when the last counts were saved
    Serial.print(F("Hourly: "));
    Serial.print(hourlyPersonCount);
    Serial.print(F(" Daily: "));
    Serial.print(dailyPersonCount);
    Serial.print(F("  Time: "));
    Serial.println(Time.timeStr(t)); // Prints time t - example: Wed May 21 01:08:47 2014
    ledState = !ledState;              // toggle the status of the LEDPIN:
    digitalWrite(blueLED, ledState);    // update the LED pin itself
  }
}

void StartStopTest(boolean startTest)  // Since the test can be started from the serial menu or the Simblee - created a function
{
 if (startTest) {
     inTest = true;
     currentHourlyPeriod = Time.hour();   // Sets the hour period for when the count starts (see #defines)
     currentDailyPeriod = Time.day();     // And the day  (see #defines)
     // Deterimine when the last counts were taken check when starting test to determine if we reload values or start counts over
     time_t unixTime = FRAMread32(CURRENTCOUNTSTIME);
     lastHour = Time.hour(unixTime);
     lastDate = Time.day(unixTime);
     dailyPersonCount = FRAMread16(CURRENTDAILYCOUNTADDR);  // Load Daily Count from memory
     hourlyPersonCount = FRAMread16(CURRENTHOURLYCOUNTADDR);  // Load Hourly Count from memory
     if (currentDailyPeriod != lastDate) {
         LogHourlyEvent();
         LogDailyEvent();
     }
     else if (currentHourlyPeriod != lastHour) {
         LogHourlyEvent();
     }
 }
 else {
     inTest = false;
     t = Time.now();
     FRAMwrite16(CURRENTDAILYCOUNTADDR, dailyPersonCount);   // Load Daily Count to memory
     FRAMwrite16(CURRENTHOURLYCOUNTADDR, hourlyPersonCount);  // Load Hourly Count to memory
     FRAMwrite32(CURRENTCOUNTSTIME, t);   // Write to FRAM - this is so we know when the last counts were saved
     hourlyPersonCount = 0;        // Reset Person Count
     dailyPersonCount = 0;         // Reset Person Count
 }
}

void LogHourlyEvent() // Log Hourly Event()
{
  time_t LogTime = FRAMread32(CURRENTCOUNTSTIME);     // This is the last event recorded - this sets the hourly period
  unsigned int pointer = (HOURLYOFFSET + FRAMread16(HOURLYPOINTERADDR))*WORDSIZE;  // get the pointer from memory and add the offset
  LogTime -= (60*Time.minute(LogTime) + Time.second(LogTime)); // So, we need to subtract the minutes and seconds needed to take to the top of the hour
  FRAMwrite32(pointer, LogTime);   // Write to FRAM - this is the end of the period
  FRAMwrite16(pointer+HOURLYCOUNTOFFSET,hourlyPersonCount);
  stateOfCharge = int(batteryMonitor.getSoC());
  FRAMwrite8(pointer+HOURLYBATTOFFSET,stateOfCharge);
  unsigned int newHourlyPointerAddr = (FRAMread16(HOURLYPOINTERADDR)+1) % HOURLYCOUNTNUMBER;  // This is where we "wrap" the count to stay in our memory space
  FRAMwrite16(HOURLYPOINTERADDR,newHourlyPointerAddr);
}

void LogDailyEvent() // Log Daily Event()
{
 time_t LogTime = FRAMread32(CURRENTCOUNTSTIME);// This is the last event recorded - this sets the daily period
 int pointer = (DAILYOFFSET + FRAMread8(DAILYPOINTERADDR))*WORDSIZE;  // get the pointer from memory and add the offset
 FRAMwrite8(pointer,Time.month(LogTime)); // The month of the last count
 FRAMwrite8(pointer+DAILYDATEOFFSET,Time.day(LogTime));  // Write to FRAM - this is the end of the period  - should be the day
 FRAMwrite16(pointer+DAILYCOUNTOFFSET,dailyPersonCount);
 stateOfCharge = batteryMonitor.getSoC();
 FRAMwrite8(pointer+DAILYBATTOFFSET,stateOfCharge);
 uint8_t newDailyPointerAddr = (FRAMread8(DAILYPOINTERADDR)+1) % DAILYCOUNTNUMBER;  // This is where we "wrap" the count to stay in our memory space
 FRAMwrite8(DAILYPOINTERADDR,newDailyPointerAddr);
}

void sendEvent(bool dailyEvent)
{
  getSignalStrength();
  int currentTemp = getTemperature();  // in degrees F
  stateOfCharge = int(batteryMonitor.getSoC()); // Percentage of full charge
  char data[256];                                         // Store the date in this character array - not global
  snprintf(data, sizeof(data), "{\"hourly\":%i, \"daily\":%i,\"battery\":%i, \"temp\":%i}",hourlyPersonCount, dailyPersonCount, stateOfCharge, currentTemp);
  Particle.publish("Send_Counts", data, PRIVATE);
  if (dailyEvent)
  {
    dailyPersonCountSent = dailyPersonCount; // This is the number that was sent to Ubidots - will be subtracted once we get confirmation
    currentDailyPeriod = Time.day();  // Change the time period
  }
  hourlyPersonCountSent = hourlyPersonCount; // This is the number that was sent to Ubidots - will be subtracted once we get confirmation
  currentHourlyPeriod = Time.hour();  // Change the time period
  dataInFlight = true; // set the data in flight flag
}

void UbidotsHandler(const char *event, const char *data)  // Looks at the response from Ubidots - Will reset Photon if no successful response
{
  // Response Template: "{{hourly.0.status_code}}"
  if (!data) {                                            // First check to see if there is any data
    Particle.publish("Ubidots Hook", "No Data");
    return;
  }
  int responseCode = atoi(data);                          // Response is only a single number thanks to Template
  if ((responseCode == 200) || (responseCode == 201))
  {
    Particle.publish("State","Response Received");
    dataInFlight = false;                                 // Data has been received
  }
  else Particle.publish("Ubidots Hook", data);             // Publish the response code
}

void getSignalStrength()
{
    CellularSignal sig = Cellular.RSSI();  // Prototype for Cellular Signal Montoring
    int rssi = sig.rssi;
    int strength = map(rssi, -131, -51, 0, 5);
    sprintf(Signal, "%s: %d", levels[strength], rssi);
}

int startStop(String command)   // Will reset the local counts
{
  if (command == "1" && !inTest)
  {
    StartStopTest(1);
    return 1;
  }
  else if (command == "0" && inTest)
  {
    StartStopTest(0);
    return 1;
  }
  else return 0;
}

int resetFRAM(String command)   // Will reset the local counts
{
  if (command == "1")
  {
    ResetFRAM();
    return 1;
  }
  else return 0;
}

int resetCounts(String command)   // Resets the current hourly and daily counts
{
  if (command == "1")
  {
    FRAMwrite16(CURRENTDAILYCOUNTADDR, 0);   // Reset Daily Count in memory
    FRAMwrite16(CURRENTHOURLYCOUNTADDR, 0);  // Reset Hourly Count in memory
    FRAMwrite8(RESETCOUNT,0);          // If so, store incremented number - watchdog must have done This
    resetCount = 0;
    hourlyPersonCount = 0;                    // Reset count variables
    dailyPersonCount = 0;
    hourlyPersonCountSent = 0;                // In the off-chance there is data in flight
    dailyPersonCountSent = 0;

    dataInFlight = false;
    return 1;
  }
  else return 0;
}

int resetNow(String command)   // Will reset the local counts
{
  if (command == "1")
  {
    System.reset();
    return 1;
  }
  else return 0;
}

int sendNow(String command) // Function to force sending data in current hour
{
  if (command == "1")
  {
    sendEvent(0);
    return 1;
  }
  else return 0;
}

int getTemperature()
{
  int reading = analogRead(tmp36Pin);   //getting the voltage reading from the temperature sensor
  float voltage = reading * 3.3;        // converting that reading to voltage, for 3.3v arduino use 3.3
  voltage /= 4096.0;                    // Electron is different than the Arduino where there are only 1024 steps
  int temperatureC = int(((voltage - 0.5) * 100));  //converting from 10 mv per degree with 500 mV offset to degrees ((voltage - 500mV) times 100) - 5 degree calibration
  temperatureF = int((temperatureC * 9.0 / 5.0) + 32.0);  // now convert to Fahrenheit
  return temperatureF;
}

void sensorISR()
{
  sensorDetect = true;                                      // sets the sensor flag for the main loop
}

resetCount and hourlyPersonoOunt (as well as all you other integer variables to be used for Particle.variable) must be a int or int32_t (can be unsigned, but will result in wrong values when exceeding the positive range of signed).

For Signal it seemt to be a memory corruption of some kind, which I haven’t found yet :wink:
But try to print out the contents of your Signal base variable at multiple locations in your code to find out where it gets corrupted.
Also mark your char* level[] as const and use snprintf() instead of sprintf() to save yourself the hassle to track down buffer violations by sprintf() :wink:

3 Likes

@ScruffR,

OK, so some progress.

  1. Have changed the Particle variables to int and that seems to have solved the issue with the numbers there. I had used uint16_t as I am writing to FRAM but, will simply cast when writing to FRAM like this: static_cast<uint16_t>(dailyPersonCount). This seems to work.

  2. Changed to snprintf() and added const to the level[] definition - this did not fix the issue but, as you pointed out, this is simply a good thing to do. The corruption issue for this string is happening as the device wakes from sleep. I refreshed the device and saw the Signal string sent correctly. Then the device woke on the hour and reported - this time with the corrupted Signal string. Interestingly, the characters are not random and seem to be the same each time it is corrupted. Is there some process that saves variables when the Particle sleeps and, perhaps, for Strings is not restored correctly on waking?

I had a spare Electron laying around which is on 0.6.2 and I swapped out the parts to see if I could eliminate the firmware as a suspect. I am seeing the same corruption of Signal on 0.6.2 so I think we can eliminate this as an issue. Changing to the other part eliminated the startup problem (having to reset from DFU to boot).

So, I think there are now two issues:

  1. Corruption of the Signal string - will start adding print statements here
  2. the PMIC is screwed up. I am hoping there is a “factory settings” program I could load to undo any of the changes to the PMIC registers that were made in the program I rand from @BDub and referenced in my original post.

Thank you for your help and please let me know if you have any more suggestions - especially on the PMIC stuff which is not well documented in the Particle Reference.

Chip

To have variables survive a deep sleep phase you may want to look at Backup RAM and the retained keyword.
So do you see the issue after wake from deep sleep or Stop Mode sleep? It makes a difference for the lifetime of variables.

You should also add a waitUntil(Particle.connected) or waitFor(Particle.connected, <timeout>) between your initial connect and the publish instruction.

I thought the PMIC chip was reset to factory settings by the Particle firmware every time the system is booted up and this is the reason we always have to reset the higher charging current and input voltage settings in setup code everytime the device starts up.

What exactly is going on with the PMIC chip?

@RWB,

I am not sure what is the issue but here are the symptoms:

  • Without anything connected to it, the Electron starts normally if a LiPo battery is connected to the board directly
  • If the Particle has more than about 20mA of components connected, it does not startup. It flashes the RGB led white once and then goes out. The 3.3V pin will put out 3.3V but the device will not boot up.
  • To boot up the device, I put it into DFU mode and then press reset. Then the Electron starts up and runs normally

All this started to happen after running that PMIC diagnostic code. Have not been able to get it back to its normal state since.

Any ideas?

Chip

This does not happen on an Electron that you did not run the Diagnostic code on?

I’ve never seen this Diagnostic code but it sounds like a setting in that code caused the trouble. Was that code useful other than the trouble it caused later?

@ScruffR,

Thank you, I will make these changes. The corruption of the Signal variable comes after the Napping state which is:

  case NAPPING_STATE: {
    if (Particle.connected())
    {
      Particle.disconnect();                                   // Disconnect from Particle in prep for sleep
      delay(3000);
      Cellular.disconnect();                                   // Disconnect from the cellular network
      delay(3000);
      Cellular.off();                                         // Turn off the cellular modem
      timeTillSleep = napDelay;
    }
    ledState = false;                                        // Turn out the light
    digitalWrite(blueLED,LOW);                               // Turn off the LED
    sensorDetect = true;                                     // Woke up so there must have been an event
    lastEvent = millis();                                    // Reset millis so we don't wake and then nap again
    int secondsToHour = (60*(60 - Time.minute()));          // Time till the top of the hour
    System.sleep(intPin,RISING,secondsToHour);               // Sensor will wake us with an interrupt
    attachInterrupt(intPin,sensorISR,RISING);                // Sensor interrupt from low to high
    state = IDLE_STATE;                                      // Back to the IDLE_STATE after a nap
    } break;

So, this is not the DEEP mode sleep.

Thanks,

Chip

@RWB,

Yes, it was helpful as I was getting a flashing red charging light. This code showed me why the light was flashing.

I have only seen this behavior on one device.

Thanks,

Chip

So if you don’t run that code on a new Electron you shouldn’t see any issues in the future?

Seems to be the simplest solution considering everything else you’re trying to fix at the moment :slight_smile:

Or do you still want or need this Diagnostic code and the features it provides?

@RWB, yes, you are right. However, I would like to see if there’s someway to bring the electron that is currently affected back to life by restoring the settings to the original defaults.

Do you know how to load the whole firmware via a JTAG interface? I don’t but that’s how I hear others wipe clean units and bring them back to life sometimes.

@RWB,

I have loaded the firmware using the DFU mode but not JTAG. Also, I think the issue is with some setting on the PMIC. Would a firmware update for the Electron overwrite all those settings too?

Chip

Not sure. They have to program the PMIC somehow and I’m not sure if a DFU upload would do that.

@ScruffR What is required to wipe an Electron clean and then load it back to factory new?

I can’t see any debug print statements in that snippet.
You should add at least one before the sleep statement and one after to see whether this really happens while sleep/on wake or anywhere prior or after that.
I still doubt System.sleep() as such causes this.

@RWB, dfu-util has no direct access to the PMIC and I don’t think there is any code in the PMIC library that would write persistent data to it.
But if you suspect there was some one-time action that had a persistent effect on the PMIC I’d tag @rickkas7.

@ScruffR Thanks.

@chipmc Share that PMIC Diagnostic code. Let’s see if there is anything in there that changes the PMIC registers.

@RWB,

Sure, here is the code I ran. It does seem to modify some registers. It also turns off a couple things that never get turned back on. This was form a post by @BDub

#include <math.h>
#include "application.h"

int pmicStatus(String args) {
    delay(3000); // build up some time for a 3-Publish burst (super hacky)

    PMIC power;
    power.begin();
    power.disableWatchdog();
    power.disableDPDM();
    // power.setInputVoltageLimit(4360); // default
    power.setInputCurrentLimit(900);     // 900mA
    power.setChargeCurrent(0,0,0,0,0,0); // 512mA
    power.setChargeVoltage(4208);        // 4.208V termination voltage
    FuelGauge fuel;
    bool LOWBATT = fuel.getAlert();
    float SOC = fuel.getSoC();
    if (LOWBATT) {
        fuel.clearAlert(); // Clear the Low Battery Alert flag if set
    }
        
    //https://github.com/spark/firmware/blob/develop/system/src/main.cpp#L287-L325
    uint8_t stat = power.getSystemStatus();
	// Read first time for previous fault
	uint8_t fault = power.getFault();
	uint8_t vbus_stat = stat >> 6; // 0 – Unknown (no input, or DPDM detection incomplete), 1 – USB host, 2 – Adapter port, 3 – OTG
	uint8_t chrg_stat = (stat >> 4) & 0x03; // 0 – Not Charging, 1 – Pre-charge (<VBATLOWV), 2 – Fast Charging, 3 – Charge Termination Done
	bool dpm_stat = stat & 0x08;   // 0 – Not DPM, 1 – VINDPM or IINDPM
	bool pg_stat = stat & 0x04;    // 0 – Not Power Good, 1 – Power Good
	bool therm_stat = stat & 0x02; // 0 – Normal, 1 – In Thermal Regulation
	bool vsys_stat = stat & 0x01;  // 0 – Not in VSYSMIN regulation (BAT > VSYSMIN), 1 – In VSYSMIN regulation (BAT < VSYSMIN)
	bool wd_fault = fault & 0x80;  // 0 – Normal, 1- Watchdog timer expiration
	uint8_t chrg_fault = (fault >> 4) & 0x03; // 0 – Normal, 1 – Input fault (VBUS OVP or VBAT < VBUS < 3.8 V),
											  // 2 - Thermal shutdown, 3 – Charge Safety Timer Expiration
	bool bat_fault = fault & 0x08;    // 0 – Normal, 1 – BATOVP
	uint8_t ntc_fault = fault & 0x07; // 0 – Normal, 5 – Cold, 6 – Hot

    String stat_str = String::format("VBUS:%d CHRG:%d DPM:%d PG:%d THERM:%d VSYS:%d", vbus_stat, chrg_stat, dpm_stat, pg_stat, therm_stat, vsys_stat);
    String fault_str = String::format("PREV - WATCHDOG:%d CHRG:%d BAT:%d NTC:%d", wd_fault, chrg_fault, bat_fault, ntc_fault);
    Particle.publish("STAT", stat_str);
    Particle.publish("PREV-FAULT", fault_str);
    
    // Read a second time for current fault
    fault = power.getFault();
    wd_fault = fault & 0x80;  // 0 – Normal, 1- Watchdog timer expiration
	chrg_fault = (fault >> 4) & 0x03; // 0 – Normal, 1 – Input fault (VBUS OVP or VBAT < VBUS < 3.8 V),
											  // 2 - Thermal shutdown, 3 – Charge Safety Timer Expiration
	bat_fault = fault & 0x08;    // 0 – Normal, 1 – BATOVP
	ntc_fault = fault & 0x07; // 0 – Normal, 5 – Cold, 6 – Hot
    fault_str = String::format("CURR - WATCHDOG:%d CHRG:%d BAT:%d NTC:%d", wd_fault, chrg_fault, bat_fault, ntc_fault);
    
    Particle.publish("CURR-FAULT", fault_str);
    
    return (int)round(SOC);
}

void setup() {
    Particle.function("stat", pmicStatus);
    
    pmicStatus("");
}

void loop() {

}

Any ideas?

Chip

@ScruffR,

Will do. Just have to bring the box back in as I have been testing it getting Solar power through a cloudy stretch here in Raleigh.

Thanks,

Chip

I’m not sure what the exact cause is but I see the code disabled the DPDM which may be the cause of what you’re seeing.

You can try to run this function to enable it again: power.enableDPDM();

Here is a list of all the PMIC functions:

@ScruffR,

With the retained key word, the issue with the Signal string getting corrupted has been fixed. Here is a screen shot which shows that ever after hundreds of “naps” and a night’s DEEP_SLEEP the Signal strength value is not corrupted.

I also changed the while(!Particle.connected()) to waitUntil(Particle.connected) as you suggested when getting reconnected to report. Can you please explain why one is better than the other?

With this, the only issue left to resolve is the PMIC settings which I will play with today.

Thanks, Chip__