Arduino Mega deleting contents of SD card and reset not working

Hello,

I'm building a high altitude balloon project using a MKR Zero as the balloon payload and an Arduino Mega 2560 for the ground station. It sends data over 433MHz LoRa every 15s and records it on an SD card at both ends.

I've been running some overnight max duration tests to check it all out and I'm finding that after a few hours (somewhere between 2 and 6) I come back to the Mega 2560 ground station and its frozen. The LCD freezes on what ever the last packet said, the buttons are no longer responsive, the reset button doesn't work and the SD card is empty all files have been deleted. This is the case when powering it through USB cable and from battery power to Vin.

I had some suspect code relating to Strings that I've (hopefully) cleaned up a bit, but still had the same result. I'll try and run some more tests with it printing data to the computer so hopefully can get an idea of when exactly it is crashing. Just strange that resetting it does nothing.

Main Code

#include <Wire.h>
#include <QMC5883LCompass.h>
#include <NMEAGPS.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <RH_RF95.h>
#include <SD.h>
#include <EEPROM.h>

#define gpsPort Serial2
#define RFM95_CS 53
#define RFM95_RST 2
#define RFM95_INT 3
#define RF95_FREQ 433.050

const int MPU_Address = 0x68;   // I2C address of the MPU-6050
const int Mag_Address = 0x0D;   // I2C address of the MPU-6050
static NMEAGPS  gps;            
LiquidCrystal_I2C lcd(0x27,20,4);
RH_RF95 rf95(RFM95_CS, RFM95_INT);
QMC5883LCompass compass;        // Compass
const byte interruptPin = 19; int SF = 8;

int32_t Lat_GPS_raw, Long_GPS_raw, Last_packet, Lastbutton1, Lastbutton2, DisplayFinT = 0, last_GPS;
bool first_packet = false, recording = false, SD_display = true, GPS_valid = false, Balloon_GPS_valid = false;
bool change_button = false, SD_present = true; String dataString;
byte screen = 1, reply = 1; char filename[16]; 
float Lat, Long, Lat2, Long2, Speed_temp, VS, V_batt_temp;

struct payload{
  uint32_t Secs_since_launch;
  int32_t Lat;
  int32_t Long;
  uint16_t GPS_Alt;
  uint16_t GPS_Speed;
  int16_t VS;   
  int16_t Temp_in;
  int16_t Temp_out;
  uint16_t V_batt;     
  uint16_t Max_Alt;            
} __attribute__((packed, aligned(1)));

payload Data = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};


void setup() {
  Setup_code();
}


void loop() {

  if(change_button){
    Lastbutton1 = millis();
    change_button = false;
    screen++;
    lcd.clear();
    if(screen >= 4) screen = 1;
  }

  if((millis() - DisplayFinT) >= 900 && first_packet){
    DisplayFinT = millis();

    if(SD_display){
      lcd.clear();
      SD_display = false;
    } 
    if(screen == 1) Display1();
    if(screen == 2) Display2();
    if(screen == 3) Display3();
    //f(screen == 4) Display4(); // Landing prediction

    /*Change SF if needed...
    if(millis() - Last_packet > 900000 && SF < 10){ // 900,000s = 15 Mins
      SF = 11;
      rf95.setSpreadingFactor(SF);
    }*/
  }

  if(digitalRead(31) && millis() - Lastbutton2 > 1000){
    
    lcd.clear();
    lcd.setCursor(0, 1);
    Lastbutton2 = millis();
    DisplayFinT = millis();
    
    if(SD_present){
      if(!recording){
        lcd.print("SD Recording Started");
      }
      else{
        lcd.print("SD Recording Stopped");
      }
      recording = !recording;
    }
    else{
      lcd.print("No SD card detected!");
    }
    delay(15);
    SD_display = true;
  }

  
  LoRa(); //Put Lora before GPS
  GPS();
  
  
} //End of main loop



void ISRbutton(){
  if(millis() - Lastbutton1 > 600){
    change_button = true;
  }
}

void GPS()

void GPS(){
  
    while (gps.available(gpsPort)) {
    gps_fix fix = gps.read();
    if (fix.valid.location) {

      GPS_valid = true;
      last_GPS = millis();
    
      Lat_GPS_raw  = fix.latitudeL();
      Long_GPS_raw = fix.longitudeL();

      Lat = Lat_GPS_raw / 10000000.0;
      Long = Long_GPS_raw / 10000000.0;

      }
    }
}

void LoRa()

void LoRa(){

  if (rf95.available())
  {

    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);
    
    if (rf95.recv((uint8_t*)&Data, &len))
    {
      first_packet = true;
      Last_packet = millis();

      Lat2 = Data.Lat / 10000000.0;
      Long2 = Data.Long / 10000000.0;
      Speed_temp = Data.GPS_Speed / 10;
      VS = Data.VS / 10.0;
      V_batt_temp = Data.V_batt / 100.0;

      if(Lat2 == 0 || Long2 == 0){
        Balloon_GPS_valid = false;
      }
      else{
        Balloon_GPS_valid = true;
      }
      

      if (recording) {
        File dataFile = SD.open(filename, FILE_WRITE);
        
        if(dataFile){
          //Write data to SD card
          dataString += String(Data.Secs_since_launch);
          dataString += ",";
          dataString += String(Lat2,5);
          dataString += ",";
          dataString += String(Long2,5);
          dataString += ",";
          dataString += String(Data.GPS_Alt);
          dataString += ",";
          dataString += String(Speed_temp,1);
          dataString += ",";
          dataString += String(VS,1);
          dataString += ",";
          dataString += String(Data.Temp_in);
          dataString += ",";
          dataString += String(Data.Temp_out);
          dataString += ",";
          dataString += String(V_batt_temp);
          dataString += ",";
          dataString += String(SF);
          dataString += ",";
          dataString += String(Lat,5);
          dataString += ",";
          dataString += String(Long,5); 
        
          dataFile.println(dataString);
          dataFile.close();

          dataString = "";
        }
      }

      

      // Send a reply
      //rf95.waitPacketSent();
      //rf95.send((uint8_t *)&reply, sizeof(reply));
  
    }
  }
}

void Setup()

void Setup_code(){

  pinMode(4, INPUT);
  Wire.begin();
  Serial.begin(115200);
  //while(!Serial){}
  delay(500);
  Serial.println("Setup Started..");
  

  //==========================MPU6050==========================//
  byte GFS_SEL = 8; // creates binary 0b00001000, this makes gyro sensitivity 500 degs/sec. 65.536 LSBs/deg/sec 

  Wire.beginTransmission(MPU_Address);
  if(Wire.endTransmission() != 0){
    Serial.println("MPU failed");
  }

  Wire.beginTransmission(MPU_Address);
  Wire.write(0x6B), Wire.write(0x00); // sends 0's to PWR MNGMT register 0x68 resetting it. 
  Wire.endTransmission();
  Wire.beginTransmission(MPU_Address);
  Wire.write(0x1B), Wire.write(GFS_SEL); // 0x1B register sets gyro scale, 
  Wire.endTransmission();

  Serial.println("MPU Started");

  //===========================Compass==============================//

  Serial.println("Compass Started");

  compass.init();
  //compass.setCalibration(-1690, 1190, -1703, 1191, -1687, 1458);
  compass.setCalibrationOffsets(-174.00, -195.00, -147.00);
  compass.setCalibrationScales(1.00, 1.04, 0.96);
  ///// CALIBRATION TEST DATA ////// 

  gpsPort.begin(38400);


  //===========================LoRa Init==============================//
  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, HIGH);

  // manual reset
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  while (!rf95.init()) {
    Serial.println("LoRa radio init failed");
    while (1);
  }
  Serial.println("LoRa radio init OK!");

  // Defaults after init are 433.0MHz, modulation GFSK_Rb250Fd250, +13dbM
  if (!rf95.setFrequency(RF95_FREQ)) {
    while (1);
  }
  //Serial.print("Set Freq to: "); Serial.println(RF95_FREQ);

  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
  // The default transmitter power is 13dBm, using PA_BOOST.
  // If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then 
  // you can set transmitter powers from 5 to 23 dBm:
  rf95.setTxPower(12, false);
  rf95.setSpreadingFactor(SF);
  rf95.setCodingRate4(5);
  rf95.setSignalBandwidth(62500);


  //============================SD Card===========================//

  if (!SD.begin(13)){
    SD_present = false;
    Serial.println(F("SD card not detected..."));
  }
  else{
    SD_present = true;
    SD_Header();
    dataString.reserve(100);
    Serial.println(F("SD OK"));
  }

  
  //============================LCD init==========================//

  uint8_t degrees2[8] = {0x1c,0x14,0x1c,0x0,0x0,0x0,0x0,0x0};
  uint8_t m_s_1[8] = {0xa,0x15,0x15,0x11,0x0,0x1,0x2,0x4};
  uint8_t m_s_2[8] = {0x1,0x2,0x4,0xb,0x14,0x2,0x1,0x6};
  
  lcd.init();                      // initialize the lcd 
  lcd.createChar(0, degrees2);
  lcd.createChar(1, m_s_1);
  lcd.createChar(2, m_s_2);
  lcd.clear();
  lcd.backlight();

  lcd.setCursor(0, 0);
  lcd.print("      Setup OK ");
  lcd.setCursor(0, 2);
  lcd.print(" Waiting for Packets");

  //============================Interrupt Pin==========================//

  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ISRbutton, RISING);

  Serial.println(F("Setup complete"));

}

void SD_Header()

void SD_Header(){

  int file_count = EEPROM.read(1);            // Read from EEPROM location 1
  if (file_count > 99 || file_count == 0){    // If number is 0 or more than 100 make it 1
    file_count = 1;
  }
  
  sprintf(filename, "File_%02d.csv", file_count);

  //if (!myFile.open(filename, O_RDWR | O_CREAT | O_AT_END)) {}

  String header = "Time (S), Latitude, Longitude, GPS_Alt (m), GPS_Speed (m/s), VS (m/s), Temp_in (C), Temp_out (C), Vbatt (V), SF, Lat_GS, Long_GS";

  File dataFile = SD.open(filename, FILE_WRITE);

  if (dataFile) {
    dataFile.println(header);
    dataFile.close();
  }

  EEPROM.write(1, file_count + 1);       // Increment EEPROM value by 1
    
}

Has anyone got any general ideas of what would cause this? The deleting files from the SD card is strange, it just wipes the whole lot. The code running on the MKR Zero is broadly similar but runs with no issues.

Thanks

Please don't chop up the code in pieces.
Make a test having the Mega powered only by USB.
Schematics fof the Mega station could help us.

I break the code into tabs so these are just the tabs copied over.

I2C: LCD, Compass, IMU
Serial: GPS
SPI: LoRa, SD

If that helps?

If you edit your opening post and add the real names of the tabs, it will probably be easier. I guess they are all ino files, there are no .h and .cpp files based on the provided code.

1 Like

Hopefully the new names make more sense.

I just can’t think what would cause the reset button to be non-responsive?

If you are using the String class on the MEGA2560, then there's a good chance that the issues you are describing are related to this.

I would suggest using ordinary null terminated c strings instead.

Hmmm. I would have thought a reset would have had an effect. By reset, do you mean the reset button or a complete power removal?

Can you post the entire code? The LCD code appears to be missing, code will not compile because Display1() is not defined.

Eliminate all usage of String objects. Due to poor memory management, each String operation progressively eats holes in memory, leading to crashes such as you are observing.

For example, replace all of this with a series of simple `dataFile.print()' statements.

          dataString += String(Data.Secs_since_launch);
          dataString += ",";
          dataString += String(Lat2,5);
          dataString += ",";
          dataString += String(Long2,5);
          dataString += ",";
          dataString += String(Data.GPS_Alt);
          dataString += ",";
          dataString += String(Speed_temp,1);
          dataString += ",";
          dataString += String(VS,1);
          dataString += ",";
          dataString += String(Data.Temp_in);
          dataString += ",";
          dataString += String(Data.Temp_out);
          dataString += ",";
          dataString += String(V_batt_temp);
          dataString += ",";
          dataString += String(SF);
          dataString += ",";
          dataString += String(Lat,5);
          dataString += ",";
          dataString += String(Long,5); 
        
          dataFile.println(dataString);
1 Like

Yup so the reset button had no effect, the only way to restart it was to remove the power and go from there.

Ah yes that is probably a lot easier. I'll change that and test it overnight tonight.

Out of curiosity, even though I reserved 100bytes for the String and cleared it after each instance do you have any idea why that would still cause it to crash over time?

Apologies, I just omitted it as all it does it display the data onto an LCD.

Display 1


void Display1(){

  //Heading Formula Calculation


  float teta1 = radians(Lat);
  float teta2 = radians(Lat2);
  float delta2 = radians(Long2 - Long);

  float y = sin(delta2) * cos(teta2);
  float x = cos(teta1)*sin(teta2) - sin(teta1)*cos(teta2)*cos(delta2);
  float bearing2object = atan2(y,x);
  bearing2object = degrees(bearing2object);// radians to degrees
  bearing2object = ( ((int)bearing2object + 360) % 360 );

  float True_HDG = Angle_Calc() + 1;

  int bearing = bearing2object - True_HDG;

  bearing = ((bearing + 360) % 360);


  //Distance to target Calculation
  
  float Lat_dist = (Lat_GPS_raw - Data.Lat) * 0.0111111;
  if(Lat_dist < 0) Lat_dist *= -1;
  float Long_scale = radians(Lat);
  float Long_dist = (Long_GPS_raw - Data.Long) * sin(Long_scale) * 0.0111111;
  if(Long_dist < 0) Long_dist *= -1;
  float Distance_calc = sqrt((Lat_dist * Lat_dist) + (Long_dist * Long_dist));

  
  float Alt = Data.GPS_Alt;


  //=============================Display================================================//

  //Lat/Long
  lcd.setCursor(1, 0);
  lcd.print(Lat2, 5);
  lcd.print(F("   "));
  lcd.println(Long2, 5);


  //////////////////Alt////////////////
  lcd.setCursor(0, 1);
  lcd.print(F("Alt "));
  if (Alt >= 10000){
    lcd.setCursor(4,1);
  } 
  if (Alt >= 1000 && Alt < 10000 || Alt <= -100){
    lcd.print(F(" "));
    lcd.setCursor(5,1); 
  } 
  if (Alt < 1000 && Alt >= 100 || Alt <= -10 && Alt > -100){
    lcd.print(F("  "));
    lcd.setCursor(6,1);
  } 
  if (Alt < 100 && Alt >= 10 || Alt < 0 && Alt > -10){
    lcd.print(F("   "));
    lcd.setCursor(7,1);
  } 
  if (Alt < 10 && Alt >= 0){
    lcd.print(F("    "));
    lcd.setCursor(8,1);
  }
  lcd.print((int)Alt);
  lcd.print(F("m"));


  ////////////////VS////////////////
  lcd.setCursor(11, 1);
  lcd.print(F("VS "));
  if (VS <= -10) lcd.setCursor(13, 1);
  else if (VS > -10 && VS < 0 || VS >= 10) lcd.setCursor(14, 1);
  else if( VS >= 0 && VS < 10){
    lcd.setCursor(14, 1);
    lcd.print(F(" "));
  }
  lcd.print(VS,1);
  lcd.write(byte(1));
  lcd.write(byte(2));


  ////////////////Distance////////////////
  lcd.setCursor(0, 2);
  lcd.print(F("Dist        ")); // Originally 8 spaces

  if(first_packet && GPS_valid && Balloon_GPS_valid){

  if(Distance_calc >= 100000){
    lcd.setCursor(6,2);
    lcd.print(Distance_calc / 1000,0);
    lcd.print(F("km "));
  }
  else if(Distance_calc > 20000){
    lcd.setCursor(7,2);
    lcd.print(Distance_calc / 1000,0);
    lcd.print(F("km "));
  }
  else if(Distance_calc >= 10000){
    lcd.setCursor(5,2);
    lcd.print(Distance_calc / 1000,1);
    lcd.print(F("km "));
  }
  else if(Distance_calc >= 1000){
    lcd.setCursor(6,2);
    lcd.print(Distance_calc / 1000,1);
    lcd.print(F("km "));
  }
  else if(Distance_calc >=100){
    lcd.setCursor(6,2);
    lcd.print((int)Distance_calc);
    lcd.print(F("m "));
  }
  else if(Distance_calc > 10){
    lcd.setCursor(7,2);
    lcd.print((int)Distance_calc);
    lcd.print(F("m "));
  }
  else{
    lcd.setCursor(8,2);
    lcd.print((int)Distance_calc);
    lcd.print(F("m "));
  }
  }


  ////////////////Bearing////////////////
  lcd.setCursor(12, 2);
  lcd.print(F("Brg "));
  if(first_packet && GPS_valid && Balloon_GPS_valid){
  if (bearing < 100) lcd.print(F("0"));
  if (bearing < 10)  lcd.print(F("0"));
  lcd.print((int)bearing);
  lcd.write(byte(0));
  }

  ////////////////Speed////////////////
  lcd.setCursor(0, 3);
  lcd.print(F("Spd   "));
  
  if( Speed_temp < 10){
    lcd.setCursor(6,3);
  }
  else if(Speed_temp >= 10 && Speed_temp < 100){
    lcd.setCursor(5,3);
  }
  else if(Speed_temp >= 100){
    lcd.setCursor(4,3);
  }
  lcd.print(Speed_temp,0);
  lcd.write(byte(1));
  lcd.write(byte(2));


  //////////////////Packet time////////////////
  lcd.setCursor(10, 3);
  lcd.print(F(" Pkt "));
  static char tBuf[20];
  long Value = (millis() - Last_packet) / 1000;
  
  if (Value < 3600){
    byte Seconds = Value % 60;
    byte minutes = (Value/60)%60;
    sprintf(tBuf,"%02um%02u",minutes, Seconds);
    lcd.print(tBuf);
  }
  else{
    byte minutes = (Value/60)%60;
    byte hours = (Value/3600)%24;
    sprintf(tBuf,"%02u:%02u", hours, minutes);
    lcd.print(tBuf);
  }

}
  

any idea why that would still cause it to crash over time?

I understand that String operations like "+" create an intermediate String object. Read more here:

1 Like

That worked a treat, thank you.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.