Hard Fault following System.sleep()

I am trying to conserve battery by putting my Photon into deep sleep. When issuing System.sleep(), my device experiences a hard fault as indicated by the red flashing SOS followed by a single red flash. This is essentially what I am executing using a timer to call the sleep every 60 seconds for testing. Eventually I will sleep for 30 minutes or so. Occasionally the photon sleeps properly without a hard fault. Unfortunately, the hard fault causes the device to reset and NOT sleep.

Timer sleepy(communicationTimeout, go_to_sleep);
void setup() {
    sleepy.start();
}
void go_to_sleep(){
  System.sleep(SLEEP_MODE_DEEP,60);
}

Could you try setting a flag in the timer and acting on that in the loop? Might be the cause, not sure.

You mean print a message to serial within the sleep function?

No, not really. Or not at all actually :wink:

In the timer function set a Boolean variable to true ‘goToSleep’ or something. Then, in the loop check if ‘goToSleep == true’ and if so, trigger the sleep function.

Timers behave like interrupts, and it’s generally considered bad practice to do any actual work inside them. Rather use them to set ‘flags’ which you can check in your loop.

1 Like

Ok that works some of the time. Now I am seeing a solid cyan sometimes when sleeping. I have a Serial.println("sleeping"); that runs properly right before this behavior. Here is what it looks like in my loop:

if (goToSleep){
Serial.println("sleeping");
System.sleep(SLEEP_MODE_DEEP,60);

}

Currently I am trying to just sleep for 60 seconds while testing.

That should not happen.

Can you post your full code?
What system version have you got on your device?
Have you got anything attached to WKP?
Can you post a video of the happening?

I am going to clean up some parts of code that might be causing some issues and then I will post if that does not resolve it. Thanks! What does a solid cyan usually indicate?

Solid cyan indicates that your code is deadlocking the application and the system thread.

Here is my code. I am not getting the hard fault again (single red blink after SOS). This occurs when the System.sleep(SLEEP_MODE_DEEP,60); runs. Firmware version 0.5.2. Everything runs fine if I remove the sleep statement.

//onewire and dallas temperature code
#include "OneWire.h"
#include "spark-dallas-temperature.h"
#define ONE_WIRE_BUS D4
#define TEMPERATURE_PRECISION 11
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress inSoilThermometer = {0x28, 0x1A, 0x11, 0xAF, 0x7, 0x0, 0x0, 0x72};//Waterproof temp sensor address

// Particle event
#define PARTICLE_EVENT 1
#define PARTICLE_EVENT_NAME "pool-logger"

// MAX17043 battery manager IC settings
#include "SparkFunMAX17043.h"
float batteryVoltage;
double batterySOC;
bool batteryAlert;

//configuration to log data to ThingSpeak
#include "ThingSpeak.h"
unsigned long myChannelNumber = 144215;  //e.g. 101992
const char * myWriteAPIKey = "<key>"; // write key here, e.g. ZQV7CRQ8PLKO5QXF

TCPClient client;
String inputstring = "";                              //a string to hold incoming data from the PC
String phstring = "";                             //a string to hold the data from the Atlas Scientific product
boolean input_string_complete = false;                //have we received all the data from the PC
boolean sensor_string_complete = false;               //have we received all the data from the Atlas Scientific product
float pH = 0;                                             //used to hold a floating point number that is the pH// last time since we sent sensor readings
int lastSend = 0;                                   //last time sensor reading sent
double InTempC = 0;//original temperature in C from DS18B20
double watertempf = 0;//converted temperature in F from DS18B20
double soc;  //battery percentage

//pH circuit configuration strings
String ledconfig = "L,0";
String phReadCont = "C,0";

// connection settings
float batterySOCmin = 95.0; // minimum battery state of charge needed for short wakeup time
unsigned long wakeUpTimeoutShort = 10; // wake up every 5 mins when battery SOC > batterySOCmin
unsigned long wakeUpTimeoutLong = 600; // wake up every 10 mins during long sleep, when battery is lower
byte bssid[6];
String bssidString = "";

// for updating software
bool waitForUpdate = false; // for updating software
unsigned long updateTimeout = 300000; // 5 min timeout for waiting for software update
unsigned long communicationTimeout = 60000; // wait 3 mins before sleeping
unsigned long bootupStartTime;

//Timer setup
unsigned long measureInterval = 15000; // can send data to thingspeak every 15s, but give the matlab analysis a chance to add data too
bool goToSleep = false;
int count = 0;
Timer sleepy(communicationTimeout, go_to_sleep);
Timer measurement(measureInterval, doTelemetry);
Timer countup(1000, counter);

// for publish and subscribe events
String eventPrefix = "poolMon";

void update18B20Temp(DeviceAddress deviceAddress, double &tempC); //predeclare to compile

void setup() {
  // DS18B20 initialization
  sensors.begin();
  sensors.setResolution(inSoilThermometer, TEMPERATURE_PRECISION);

  //initialize pH sensor and Serial port
  Serial.begin(9600);
  Serial1.begin(9600);                                //set baud rate for software serial port_1 to 9600
  inputstring.reserve(10);                            //set aside some bytes for receiving data from the PC
  phstring.reserve(30);                           //set aside some bytes for receiving data from Atlas Scientific product

  Particle.variable("pHVar", String(pH));
  Particle.variable("TempVar", watertempf);
  Particle.variable("BattVar", batterySOC);

  //set the LED on the pH sensor chip
  Serial1.print(ledconfig);
  Serial1.print('\r');
  Serial1.print(phReadCont);
  Serial1.print('\r');

  // Set up the MAX17043 LiPo fuel gauge:
  lipo.begin(); // Initialize the MAX17043 LiPo fuel gauge

  // Quick start restarts the MAX17043 in hopes of getting a more accurate
  // guess for the SOC.
  lipo.quickStart();

  ThingSpeak.begin(client);
  Particle.subscribe(eventPrefix, eventHandler);
  Particle.publish(eventPrefix + "/pHSensor/startup", "v2.1.3"); // subscribe to this with the API like: curl https://api.particle.io/v1/devices/events/temp?access_token=1234
  sleepy.start();
  measurement.start();
  countup.start();

  doTelemetry(); // always take the measurements at least once
}

//--------------------------------------------------------------------
//if the hardware serial port_0 receives a char
//read the string until we see a <CR>
//set the flag used to tell if we have received a completed string from the PC
void serialEvent() {
  inputstring = Serial.readStringUntil(13);
  input_string_complete = true;
}

//reads data from pH sensor
//read the string until we see a <CR>
//set the flag used to tell if we have received a completed string from the PC
void serialEvent1() {
  phstring = Serial1.readStringUntil(13);
  sensor_string_complete = true;
}

void go_to_sleep(){
  goToSleep = true;
}

void loop() {
  if (goToSleep){
    Serial.println("sleeping");
    Particle.publish(eventPrefix + "/pHSensor/sleep", "true");
    count = 0;
    goToSleep = false;
    System.sleep(SLEEP_MODE_DEEP,60);
  }
  if (input_string_complete == true) {                //if a string from the PC has been received in its entirety
    Serial1.print(inputstring);                       //send that string to the Atlas Scientific product
    Serial1.print('\r');                              //add a <CR> to the end of the string
    inputstring = "";                                 //clear the string
    input_string_complete = false;                    //reset the flag used to tell if we have received a completed string from the PC
  }
  if (sensor_string_complete == true) {               //if a string from the Atlas Scientific product has been received in its entirety
    Serial.println(phstring);                         //send that string to the PC's serial monitor
    if (isdigit(phstring[0])) {                   //if the first character in the string is a digit
      ThingSpeak.setField(2, phstring);
      Particle.publish(eventPrefix + "/pHSensor/pH", phstring);
      pH = phstring.toFloat();                    //convert the string to a floating point number so it can be evaluated by the Arduino
    }
    phstring = "";                                  //clear the string:
    sensor_string_complete = false;
  }
}
//helps to keep track of the timing while testing
void counter(){
  Serial.println(count++);
}
//if update flag is sent, increase communication time to allow update to be sent
//This also allows the pH circuit configurations to be sent via a publish event
void eventHandler(String event, String data)
{
  // to publish update: curl https://api.particle.io/v1/devices/events -d "name=update" -d "data=true" -d "private=true" -d "ttl=60" -d access_token=1234
  if (event == eventPrefix + "/pHSensor/update") {
    if (data == "true"){
      waitForUpdate = true;
      sleepy.changePeriod(600000);
      Serial.println("wating for update");
      Particle.publish(eventPrefix + "/pHSensor/updateConfirm", "waiting for update");
    }
  }
  if (event == eventPrefix + "/pHSensor/cfg") {
    Serial.println("sending setting to pH sensor:" + data);
    Serial1.print(data);
  }
  Serial.print(event);
  Serial.print(", data: ");
  Serial.println(data);
}
void getTemp(){
  Serial.println("getting temperature reading");
  //get temp from DS18B20
  sensors.requestTemperatures();
  update18B20Temp(inSoilThermometer, InTempC);
  //Every so often there is an error that throws a -127.00, this compensates
  if(InTempC < -100)
    watertempf = watertempf;//push last value so data isn't out of scope
  else
    watertempf = (InTempC * 9)/5 + 32;//else grab the newest, good data

  Serial.print("  Temperature = ");
  Serial.print(InTempC);
  Serial.print(" Celsius, ");
  Serial.print(watertempf);
  Serial.println(" Fahrenheit");
}

//---------------------------------------------------------------
void update18B20Temp(DeviceAddress deviceAddress, double &tempC)
{
  tempC = sensors.getTempC(deviceAddress);
}
//---------------------------------------------------------------

void doTelemetry() {
    Particle.process();
    // publish we're still here
    Serial.println("started telemetry");
    Particle.publish(eventPrefix + "/pHSensor/online", "true");

    Serial.println("triggering reading of pH");
    Serial1.print('R');
    Serial1.print('\r');

    //reject bad readings and try again
    int tempcount = 1;
    watertempf = 0;
    while ((watertempf < 70 || watertempf > 95) && tempcount < 15) {
        tempcount++;
        getTemp();
    }
    ThingSpeak.setField(1, String(watertempf));

    // read battery states
    batteryVoltage = lipo.getVoltage();
    ThingSpeak.setField(3, batteryVoltage);
    // lipo.getSOC() returns the estimated state of charge (e.g. 79%)
    batterySOC = lipo.getSOC();
    ThingSpeak.setField(4, String(batterySOC));

    ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);

    Particle.publish(eventPrefix + "/pHSensor/temp", String(watertempf));
    Particle.publish(eventPrefix + "/pHSensor/batterySOC", String(batterySOC));
}

One thing you should do is to put a five second wait between your publish and the sleep statement.
I’d do this

  for(uint32_t ms=millis(); millis() - ms < 5000; Particle.process());

Other than that I’d have to plough through your whole code to see more, but that’d take time.
When do you get the solid cyan? (on sleep or on wake)

I wouldn’t do this either

  float pH;
  Particle.variable("pHVar", String(pH));

Rather make pH a double and expose that as Particle.variable() - you can always typecast wherever you need it to be a float, but not with Particle.variable().

You might also want to add some sanity check to your serialEvent() handlers, since readStringUntil() might actually timeout and then you won’t have a complete string.

1 Like

I removed it altogether. That was just there for testing at this point. Pros/Cons of just using a delay? Even with it removed I am seeing a hard fault (no longer the solid cyan) when the sleep is called. The only thing I can think of is to disable portions to see what might be causing the fault. It is weird to me that this is happening when the sleep is triggered.

Was already thinking of changing that… done.

@ScruffR and @Moors7: I figured it out. It turns out that my measurement timer was firing every 15 seconds and this was triggering at the same time as the sleep was triggered. By staggering the sleep so it does not trigger at the same time as one of the measurements, I no longer get the hard fault. For example, sleep after 70 seconds instead of at 60 seconds when the measurement timer was triggering.

Thanks for both of your help!

1 Like