Particle photon 2 stuck on non breathing green or no RGB

Hello everyone,

I'm currently working on a project involving a Particle Photon 2 that needs to store data offline and then send it via HTTP when connected to the internet. To achieve this, I'm using external flash memory.
However, I've encountered an issue where the Photon seems to get stuck after running for a while in offline mode. The RGB LED shows either a non-breathing green or no light at all.

I suspect that the problem might be related to the way I'm managing the external flash memory or the way I'm handling the internet connectivity check.

I'd appreciate any insights or suggestions on what might be causing this issue and how I can resolve it.

// This #include statement was automatically added by the Particle IDE.
#include <RTClibraryDS3231_DL.h>

#include "DFRobot_SHT20.h"
#include <DustSensor.h>

// This #include statement was automatically added by the Particle IDE.
#include <SpiffsParticleRK.h>

// This #include statement was automatically added by the Particle IDE.
#include <thermistor-library.h>

#include "Particle.h"

#include "Wire.h"

#define CHUNK_SIZE 100
#define led D7

DustSensor dustSensor;
String PM25 ="";
String PM10 ="";
//////////////////////////////////////
DFRobot_SHT20    sht20;
String Humidity ="";
String Temperature ="";
//////////////////////////////////////
RTC_DS3231 rtc;


//////////////////////////////////////

const int16_t SCD_ADDRESS = 0x62;
int status = 0;

int thermPin = A1;
int thermRes = 8000;
Thermistor Thermistor(thermPin, thermRes);
String CO2= "";

bool isButtonPressed = false;
const int buttonPin = A0;
SpiFlashWinbond spiFlash(SPI1, D5);
SpiffsParticle fs(spiFlash);


unsigned long previousMillis = 0; // Variable to store the last time a message was sent
const long interval = 60000;      // Interval in milliseconds (1 minute) 

//PM Sensor timeout
unsigned long sensorTimeout = 5000; // Timeout value in milliseconds
unsigned long sensorStartTime;

//////////////////////


#define ONE_DAY_MILLIS (24 * 60 * 60 * 1000)
unsigned long lastSync = millis();
char data[800] = "";
TCPClient client;

String server = "31da7296e5e88ed99a06e8c9fb9eca30.m.pipedream.net"; // Replace with your server URL

unsigned long lastConnectionTime = 0;
unsigned long lastUpdateTime = 0;
const unsigned long postingInterval = 120L * 1000L;
const unsigned long updateInterval = 15L * 1000L;


STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
//SYSTEM_MODE(MANUAL);                                                                                 // enabling system thread , sram memory,manual mode
SYSTEM_THREAD(ENABLED);
retained int Counter;

String getMacAddressAsString() {
    byte mac[6];
    WiFi.macAddress(mac);

    String macAddress;
    for (int i = 0; i < 6; ++i) {
        macAddress += String(mac[i], HEX);
        if (i < 5) {
            macAddress += ":";
        }
    }
    return macAddress;}

void setup() {
    Serial.begin();

    dustSensor.begin();
    Thermistor.begin();
    spiFlash.begin();
    Wire.begin();
   /////////
    sht20.initSHT20();                                  // Init SHT20 Sensor
    delay(100);
    sht20.checkSHT20();                                 // Check SHT20 Sensor
   ////////////
   
    if (!rtc.begin()) {
        Serial.println("Couldn't find RTC");
        Serial.flush();
        abort();
    }

    if (rtc.lostPower()) {
        Serial.println("RTC lost power, setting the time!");
        rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }
    
    // rtc.adjust(DateTime(2024, 3, 27, 11, 25, 0)); // Adjust as needed
   ////////////
    pinMode(led,OUTPUT);
    pinMode(buttonPin, INPUT_PULLUP);

    fs.withPhysicalSize(16 * 1024 * 1024);
    s32_t res = fs.mountAndFormatIfNecessary();

    if (res == SPIFFS_OK) {
        SpiffsParticleFile file = fs.openFile("data.txt", SPIFFS_O_APPEND | SPIFFS_O_CREAT | SPIFFS_O_WRONLY);
    }
}

void loop() {
    unsigned long currentMillis = millis();  // Get the current time
    if ((currentMillis - previousMillis >= interval) && (status == 0) && (Particle.connected()==false))                                                    // Particle disconnected condition 
    {  // Check if it's time to execute
        previousMillis = currentMillis;  // Save the last execution time
       
        //PMS 
       
        sensorStartTime = millis(); // Record the start time for sensor operation
        while (millis() - sensorStartTime < sensorTimeout) {
        if(dustSensor.listen()) {
        // Read the data from the sensor
         pms5003data data = dustSensor.readData();
         PM25 = String(data.pm25_standard);
         PM10 = String(data.pm100_standard);
         break;
       }
       }
       if (millis() - sensorStartTime >= sensorTimeout) {

        System.reset();   // Add error handling or recovery mechanism here
        }

        //PMS END

         
        //SHT20
       float humd = sht20.readHumidity();                  // Read Humidity
       float temp = sht20.readTemperature();
       Humidity = String::format("%.1f", humd);
       Temperature = String::format("%.1f", temp);  
        //


        //SCD41 starting
        
    int co2;
    uint8_t data[2];
    Wire.beginTransmission(SCD_ADDRESS);
    Wire.write(0x21);
    Wire.write(0xb1);
    Wire.endTransmission();
    delay(10);
    Wire.requestFrom(SCD_ADDRESS, 2);
    int i = 0;
    while (Wire.available() && i < 2) {
      data[i] = Wire.read();
      i++;
    }
    if (i == 2) {
      co2 = (int)((uint16_t)data[0] << 8 | data[1]);
      CO2 = String(co2); 
    } else {
      // Error handling if data not available
      co2 = -1; // Or any other error code
      CO2 = String(co2);
    }



        //SCD41 ending
        // Your code to run every 1 minute
        String macAddress = getMacAddressAsString();

        DateTime now = rtc.now();
        String currentTime = String(now.unixtime());
        float temperatureValue = Thermistor.getTempC();
        String currentTemperature = String::format("%.1f", temperatureValue);
 
        // Save current time and temperature to flash memory
        saveDataToFlash(currentTime, macAddress, currentTemperature, CO2,PM25,PM10,Temperature,Humidity);
        
    }

    
    if ((digitalRead(buttonPin) == 0)&&(Particle.connected()==true)) 
    {
        if (!isButtonPressed) {
            isButtonPressed = true;
            status = 1; 
            readAndPostDataFromFlash();
        }
    } else {
        isButtonPressed = false;
    }

    // Handle TCPClient connection status
    if ((!client.connected())&&(Particle.connected()==true)) {
        // Attempt to reconnect
        if (client.connect(server, 80)) {
            Particle.publish("Connected to server");
        } else {
            Particle.publish("Failed to connect to server");
        }
    }

    // No need to call client.loop() as it does not exist
}



void saveDataToFlash(String timestamp,String macAddress,String temperature,String CO2,String PM25,String PM10,String Temperature, String Humidity) {
    Counter = Counter + 1;
    SpiffsParticleFile file = fs.openFile("data.txt", SPIFFS_O_APPEND | SPIFFS_O_WRONLY);
    if (file.isValid()) {
        // Concatenate all data fields together with a delimiter
        String data =  "$"+(String(Counter)) +","+timestamp + "," + macAddress + "," + temperature +","+Temperature+ ","+Humidity+","+CO2+","+PM25+","+PM10+"#"+"/n";
        
        // Save the concatenated data string
        file.print(data);
        file.close();
        data = "";
     //   Particle.publish("Data saved in flash",data);
    
    } else {
   //   Particle.publish("Failed to open file for appending!");
    }
}



void updateData() {
    strcpy(data, "");
    strcat(data, String(Time.local()));
    strcat(data, ",");
    float temperatureValue = Thermistor.getTempC();
    strcat(data, String::format("%.1f", temperatureValue));
    strcat(data, "\n");

    if (millis() - lastConnectionTime >= postingInterval) {
        httpRequest(data);
        data[0] = '\0';
    }
    lastUpdateTime = millis();
}


bool httpRequest(const char* csvBuffer) {
    client.stop();

    if (client.connect(server, 80)) {
        client.println("POST / HTTP/1.1");
        client.println("Host: " + server);
        client.println("User-Agent: Particle");
        client.println("Content-Type: text/plain");
        client.print("Content-Length: ");
        client.println(strlen(csvBuffer));
        client.println();
        client.println(csvBuffer);
        return true; // Return true indicating successful connection and request sending
    } else {
        Serial.println("Failed to connect to server");
        return false; // Return false indicating failure to connect
    }
}



void readAndPostDataFromFlash() {
    digitalWrite(led, HIGH);
    delay(2000);
    digitalWrite(led, LOW);

    SpiffsParticleFile file = fs.openFile("data.txt", SPIFFS_O_RDONLY);
    if (file.isValid()) {
        String chunk;
        while (file.available()) {
            chunk = file.readStringUntil('/n');
            httpRequest(chunk.c_str());
            Particle.publish("Data from flash",chunk);
            delay(500);
            Particle.process();
        }
        file.close();
        eraseFlashMemory();
        Particle.publish("FErased");
        Counter = 0;
        status = 0;
    } else {
        Serial.println("Flash error");
    }
}





void eraseFlashMemory() {
    if (fs.remove("data.txt")) {
        Serial.println("Flash Memory Erased");
    } else {
        Serial.println("Failed to erase flash memory!");
    }
    delay(1000);
    SpiffsParticleFile file = fs.openFile("data.txt", SPIFFS_O_APPEND | SPIFFS_O_CREAT | SPIFFS_O_WRONLY);
}

It's not obvious what's going wrong. Typically if the RGB LED stops breathing it will show the last color, which in your case would be green if not connected, or it could also be off, if the lock up occurred while the LED was in the off phase of breathing. This is typically caused by deadlock caused by unsafe use of a mutex, or memory corruption caused by using freed memory or overwriting the bounds of a memory block. You will probably end up having to comment out the various parts of your code to isolate where the problem is.

This is not the problem, however since it looks like your upload occurs on demand, it would make sense to use SYSTEM_MODE(SEMI_AUTOMATIC) and only connect to Wi-Fi and the cloud when attempting to upload. The rest of the time the device would stay offline and breathe white instead of blinking green continuously.

It looks like you might be exceeding the publish limit. This isn't related to the lock up, but you can only publish 4 events in 4 seconds, and the readAndPostDataFromFlash appears to exceed that. Also it will use a large number of data operations.

You should convert all of your Serial.print to Log.info commands. See this post about it. Since the print calls is not thread safe, there is a possibility it could cause the device to crash.

1 Like

I was trying to find the error in the code by commenting various parts in the code.
But even after commenting all the functions and the entire code inside void loop and void setup, it seems to go into the deadlock again.

// This #include statement was automatically added by the Particle IDE.
#include <RTClibraryDS3231_DL.h>

#include "DFRobot_SHT20.h"
#include <DustSensor.h>

// This #include statement was automatically added by the Particle IDE.
#include <SpiffsParticleRK.h>

// This #include statement was automatically added by the Particle IDE.
#include <thermistor-library.h>

#include "Particle.h"

#include "Wire.h"

#define led D7


DustSensor dustSensor;
String PM25 ="";
String PM10 ="";
//////////////////////////////////////
DFRobot_SHT20    sht20;

//////////////////////////////////////
RTC_DS3231 rtc;


//////////////////////////////////////

const int16_t SCD_ADDRESS = 0x62;
int status = 0;

int thermPin = A1;
int thermRes = 8000;
Thermistor Thermistor(thermPin, thermRes);
String CO2= "";

bool isButtonPressed = false;
const int buttonPin = A0;
SpiFlashWinbond spiFlash(SPI1, D5);
SpiffsParticle fs(spiFlash);  



unsigned long previousMillis = 0; // Variable to store the last time a message was sent
const long interval = 60000;      // Interval in milliseconds (1 minute) 

//PM Sensor timeout
unsigned long sensorTimeout = 5000; // Timeout value in milliseconds
unsigned long sensorStartTime;    

//////////////////////  


TCPClient client;
String server = "31da7296e5e88ed99a06e8c9fb9eca30.m.pipedream.net"; // Replace with your server URL



//STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
//SYSTEM_MODE(SEMI_AUTOMATIC);                                                                                 // enabling system thread , sram memory,manual mode
SYSTEM_THREAD(ENABLED);
//retained int Counter;
int Counter;

String getMacAddressAsString() {
    byte mac[6];
    WiFi.macAddress(mac);

    String macAddress;
    for (int i = 0; i < 6; ++i) {
        macAddress += String(mac[i], HEX);
        if (i < 5) {
            macAddress += ":";
        }
    }
    return macAddress;}  
    

Even after having only declarations followed by an empty void setup and void loop, the particle seems to get stuck while in offline mode. I also wanted the device to get automatically connected to internet when it gets near to the wifi network. I'm wondering if this could be a hardware issue or if there's something else I should be considering.

I'd suspect you should get a build error here.

You'd also consider changing const long interval to const unsigned long when you want to use it in conjunction with other uint variables.

And then you might want to check the constructors of the libraries you are using.
They will be called on construction of the objects, even with empty setup() and loop() functions.

1 Like

Thanks for noticing this. btw i ddn get any build error for this instead i was getting proper values for all sensors when i tested the code in online mode where i was doing particle process for every process. This issue occurs only in offline mode where its collecting data in every 1 min from few I2C sensors and one uart sensor and saving it in the external spi flash. when it gets connected to wifi and upon a button press data is taken out of the external flash and pushed via http.

if it is a constructor problem then isn't this supposed to happen in online mode as well ?

In the datasheet its mentioned that the power should not exceed 500mA,Where i was using a adaptor rated at 5v 2amps.
Also i was made a mistake by connecting 4 different sensors to the device 3.3v pin, i think this issue was happening in offline mode when particle was searching for wifi networks and due to current issue the system was crashing.
Is there something else I should be considering ?

Yes, insufficient power can cause those symptoms as well.

There are two parts to this:

  • The power supplied to the USB port
  • The power available on from the onboard 3V3 regulator

It seems that you have sufficient power for the first, but if you have peripheral devices that require more than 150 mA typical (200 mA maximum) on 3V3, the built-in regulator will not be sufficient to power them.

1 Like

Thanks for the response, I think have figured out what's happening inside the photon.
This lockup issue was happening only when the particle is searching for a Wi-Fi network to connect to, i have modified the code into manual mode where upon a button press photon will start searching for Wi-Fi connection and push all the internally stored data. Which worked fine, photon never locked up, it was showing breathing white and storing all the data in internal memory.

But as the client requirement is to get the device connected and share all data without any physical intervention, i have modified the same code again, kept a constant interval to search for Wi-Fi connections and time out after 10 seconds if no Wi-Fi networks are found. Even in this 10 second where i am calling particle.connect(), device gets stuck after sometime. Any suggestions for this ?, i am also attaching my code here

#include <DustSensor.h>
// This #include statement was automatically added by the Particle IDE.
#include <RTClibraryDS3231_DL.h>
// This #include statement was automatically added by the Particle IDE.
#include <SpiffsParticleRK.h>
#include <thermistor-library.h>
#include "Particle.h"
#include "Wire.h"

#define led D7

DustSensor dustSensor;

//////////////////////////////////////
RTC_DS3231 rtc;
String CO2 = "";
String PM25 = "";
String PM10 = "";
//////////////////////////////////////

const int16_t SCD_ADDRESS = 0x62;

int thermPin = A1;
int thermRes = 8000;
Thermistor Thermistor(thermPin, thermRes);

bool isButtonPressed = false;
const int buttonPin = A0;
SpiFlashWinbond spiFlash(SPI1, D5);
SpiffsParticle fs(spiFlash);


unsigned long previousMillis = 0; // Variable to store the last time a message was sent
const long interval = 60000;      // Interval in milliseconds (1 minute) 

unsigned long previousMillis1 = 0; // Variable to store the last time a message was sent
const long interval1 = 65000;

int status = 0;
//PM Sensor timeout
unsigned long sensorTimeout = 5000; // Timeout value in milliseconds
unsigned long sensorStartTime;

//////////////////////


char data[800] = "";
TCPClient client;

String server ="Server"; // Replace with your server URL


SYSTEM_MODE(MANUAL);                                                                                 // enabling system thread , sram memory,manual mode
SYSTEM_THREAD(ENABLED);

String getMacAddressAsString() {
  byte mac[6];
  WiFi.macAddress(mac);

  // Convert MAC address to string without colons
  String macStr = "";
  for (int i = 0; i < 6; i++) {
  macStr += String::format("%02X", mac[i]);
  }

  Serial.println("MAC Address:");
  Serial.println(macStr);
  return macStr;
}

void setup() {
    Serial.begin();
    dustSensor.begin();
    Thermistor.begin();
    spiFlash.begin();
    Wire.begin();

    if (!rtc.begin()) {
     //   Serial.println("Couldn't find RTC");
     //   Serial.flush();
        abort();
    }

    if (rtc.lostPower()) {
     //   Serial.println("RTC lost power, setting the time!");
        rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
     }
    
   //  rtc.adjust(DateTime(2024, 4, 8, 3, 44, 0)); // Adjust as needed
   ////////////
    pinMode(led,OUTPUT);
    pinMode(buttonPin, INPUT_PULLUP);

    fs.withPhysicalSize(16 * 1024 * 1024);
    s32_t res = fs.mountAndFormatIfNecessary();

    if (res == SPIFFS_OK) {
        SpiffsParticleFile file = fs.openFile("data.txt", SPIFFS_O_APPEND | SPIFFS_O_CREAT | SPIFFS_O_WRONLY);
    }
}

void loop() {
    unsigned long currentMillis = millis();  // Get the current time
    if ((currentMillis - previousMillis >= interval) && (Particle.connected()==false)&&(status == 0))                                                    // Particle disconnected condition 
    {  // Check if it's time to execute
        previousMillis = currentMillis;  // Save the last execution time
       
        //PMS 
      sensorStartTime = millis(); // Record the start time for sensor operation
      while (millis() - sensorStartTime < sensorTimeout) {
      if(dustSensor.listen()) {
        // Read the data from the sensor
         Serial.println("PM SENSOR");
         pms5003data data = dustSensor.readData();
         PM25 = String(data.pm25_standard);
         PM10 = String(data.pm100_standard);
         Serial.println(PM10);
         Serial.println(PM25);
         break;
       }
       }
       if (millis() - sensorStartTime >= sensorTimeout) {
            Serial.println("PM sensor read timeout or error");
            // Add error handling or recovery mechanism here
        }
   
        //PMS END
        
        //SCD41 starting
    int co2;
    uint8_t data[2];
    Wire.beginTransmission(SCD_ADDRESS);
    Wire.write(0x21);
    Wire.write(0xb1);
    Wire.endTransmission();
    delay(10);
    Wire.requestFrom(SCD_ADDRESS, 2);
    int i = 0;
    while (Wire.available() && i < 2) {
      data[i] = Wire.read();
      i++;
    }
    if (i == 2) {
      co2 = (int)((uint16_t)data[0] << 8 | data[1]);
      CO2 = String(co2);
      Serial.println(CO2);
    } else {
      // Error handling if data not available
      co2 = -1; // Or any other error code
    }                                                            

         //SCD41 ending
        // Your code to run every 1 minute
        String macAddress = getMacAddressAsString();
        DateTime now = rtc.now();
        String currentTime = String(now.unixtime());
        float temperatureValue = Thermistor.getTempC();
        String currentTemperature = String::format("%.1f", temperatureValue);

        // Save current time and temperature to flash memory
        saveDataToFlash(currentTime,macAddress,currentTemperature,CO2,PM25,PM10); 
        Particle.connect();
    }
        unsigned long currentMillis1 = millis();
    if ((currentMillis1 - previousMillis1 >= interval1)&&(status == 0)) { // Check if it's time to execute
        previousMillis1 = currentMillis1;  // Save the last execution time
        Serial.println("2 Min timer");
        Particle.disconnect();
        WiFi.off();

    }
    if(Particle.connected()==true)
    {
     status = 1;
     readAndPostDataFromFlash(); 
     Particle.disconnect();
     WiFi.off();
     status = 0;
    }
    }



void saveDataToFlash(String timestamp,String macAddress,String temperature,String CO2,String PM25,String PM10) {
    SpiffsParticleFile file = fs.openFile("data.txt", SPIFFS_O_APPEND | SPIFFS_O_WRONLY);
    if (file.isValid()) {
        // Concatenate all data fields together with a delimiter
        String data =  "$"+ timestamp + "," + macAddress + "," + temperature +","+"10.1"+","+PM25+","+PM10+","+CO2+"#"+"*";
        Serial.println(data);
        // Save the concatenated data string
        file.print(data);
        file.close();
        data ="";
    } else {
     Serial.println("Flash failed");
    }
}



bool httpRequest(const char* csvBuffer) {
    client.stop();

    if (client.connect(server,80)) {
        client.println("POST / HTTPS/1.1");
        client.println("Host: " + server);
        client.println("User-Agent: Particle");
        client.println("Content-Type: text/plain");
        client.print("Content-Length: ");
        client.println(strlen(csvBuffer));
        client.println();
        client.println(csvBuffer);
        return true; // Return true indicating successful connection and request sending
    } else {
        Serial.println("Failed to connect to server");
        return false; // Return false indicating failure to connect
    }
}



void readAndPostDataFromFlash() {
    digitalWrite(led, HIGH);
    delay(2000);
    digitalWrite(led, LOW);

    SpiffsParticleFile file = fs.openFile("data.txt", SPIFFS_O_RDONLY);
    if (file.isValid()) {
        String chunk;
        while (file.available()) {
            chunk = file.readStringUntil('*');
            httpRequest(chunk.c_str());
            Particle.publish("Data from flash",chunk);
            delay(500);
            Particle.process();
        }
        file.close();
        eraseFlashMemory();
        Particle.publish("FErased");
    } else {
        Serial.println("Flash error");
    }
}





void eraseFlashMemory() {
    if (fs.remove("data.txt")) {
        Serial.println("Flash Memory Erased");
    } else {
        Serial.println("Failed to erase flash memory!");
    }
    delay(1000);
    SpiffsParticleFile file = fs.openFile("data.txt", SPIFFS_O_APPEND | SPIFFS_O_CREAT | SPIFFS_O_WRONLY);
}