Arduino stops after a few hours

My code works fine for a few hours but then stops. At this point it's mostly data logging gps and environmental sensors. I have the GPS hardware and software running on Serial2. I'm wondering why it stops after a while. It usually stops after the line "Serial.println(dataString);" at the end.

#include <SPI.h>
#include <Wire.h>
#include <RH_RF95.h>
#include <Adafruit_GPS.h>
#include <Adafruit_SHT31.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_LIS3MDL.h>
#include <Adafruit_LSM6DS33.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "RTClib.h"
#include "avr/dtostrf.h"
#define ONE_WIRE_BUS 15
#include <Servo.h>
#include <Arduino.h>   // required before wiring_private.h
#include "wiring_private.h" // pinPeripheral() functionRTC_PCF8523 rtc;
#include <SD.h>
#include <Adafruit_GPS.h>
Servo servo;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
RTC_PCF8523 rtc;

Uart Serial2 (&sercom1, 11, 10, SERCOM_RX_PAD_0, UART_TX_PAD_2);

void SERCOM1_Handler() {
  Serial2.IrqHandler();
}

// what's the name of the hardware serial port?
#define GPSSerial Serial2

// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO false

uint32_t timer = millis();

Adafruit_SHT31 sht30;  // humidity
Adafruit_BMP280 bmp280; // use I2C interface
Adafruit_LIS3MDL lis3mdl;   // magnetometer
Adafruit_LSM6DS33 lsm6ds33; // accelerometer, gyroscope

#define RFM95_CS 8
#define RFM95_RST 4
#define RFM95_INT 3

#define RF95_FREQ 433.0

// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);

int MaxAlt = 0;
int MaxSpd = 0;
int MaxITem = 0;
int MinITem = 10000;
int MaxHum = 0;
int f = 0;
int a = 1017;
float Fahrenheit = 0;
int MaxOTem = 0;
int MinOTem = 10000;
int MaxBMPAlt = 0;

void setup() {

  sht30.begin();
  bmp280.begin();
  lis3mdl.begin_I2C();
  lsm6ds33.begin_I2C();
  servo.attach(5);
  servo.write(90);
  rtc.start();
  SD.begin(10);
  pinMode(LED_BUILTIN, OUTPUT);
  sensors.begin();
  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, HIGH);

  Serial.begin(115200);
  Serial2.begin(115200);
  GPS.begin(9600);
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
  pinPeripheral(10, PIO_SERCOM);
  pinPeripheral(11, PIO_SERCOM);
  delay(1000);

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

  while (!rf95.init()) {
    while (1);
  }

  if (!rf95.setFrequency(RF95_FREQ)) {
    while (1);
  }

  bmp280.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */
                     Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                     Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                     Adafruit_BMP280::FILTER_X16,      /* Filtering. */
                     Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */

  rf95.setTxPower(23, false);
  if (!SD.begin(10)) {
    Serial.println("Card failed");
  }
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
  }
  if (! rtc.initialized() || rtc.lostPower()) {
    Serial.println("RTC is NOT initialized, let's set the time!");
  }
}

void loop() {
  sensors.requestTemperatures();
  Fahrenheit = sensors.toFahrenheit(sensors.getTempCByIndex(0));
  if (Fahrenheit > MaxOTem) {
    MaxOTem = Fahrenheit;
  }
  if (Fahrenheit < MinOTem) {
    MinOTem = Fahrenheit;
  }
  if (GPS.altitude > MaxAlt) {
    MaxAlt = GPS.altitude;
  }
  if (GPS.speed > MaxSpd) {
    MaxAlt = GPS.speed;
  }
  if (bmp280.readTemperature() > MaxITem) {
    MaxITem = bmp280.readTemperature();
  }
  if (bmp280.readTemperature() < MinITem) {
    MinITem = bmp280.readTemperature();
  }
  if (bmp280.readAltitude(a) > MaxBMPAlt) {
    MaxBMPAlt = bmp280.readAltitude(a);
  }
  if (sht30.readHumidity() > MaxHum) {
    MaxHum = sht30.readHumidity();
  }

  sensors_event_t temp_event, pressure_event;
  //bmp_temp->getEvent(&temp_event);
  //bmp280_pressure->getEvent(&pressure_event);
  sensors_event_t accel;
  sensors_event_t gyro;
  sensors_event_t temp;
  lsm6ds33.getEvent(&accel, &gyro, &temp);
  lis3mdl.read();
  DateTime now = rtc.now();

  float magnetic_x, magnetic_y, magnetic_z;
  float Lo = GPS.longitude;
  float La = GPS.latitude;
  int Al = GPS.altitude;
  int Sp = GPS.speed;
  int humidity = sht30.readHumidity();
  float temperature = temp_event.temperature;
  float accel_x, accel_y, accel_z;
  float gyro_x, gyro_y, gyro_z;

  delay(1000);

  // read data from the GPS in the 'main loop'
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
  if (GPSECHO)
    if (c) Serial.print(c);
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences!
    // so be very wary if using OUTPUT_ALLDATA and trying to print out data
    Serial.print(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false
    if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false
      return; // we can fail to parse a sentence in which case we should just wait for another
  }

  // approximately every 2 seconds or so, print out the current stats
  if (millis() - timer > 2000) {
    timer = millis(); // reset the timer

    if (GPS.fix) {
      Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
      Serial.print(", ");
      Serial.print(GPS.longitude, 4); Serial.println(GPS.lon);
      Serial.print("kn: "); Serial.println(GPS.speed);
      Serial.print("Altitude: "); Serial.println(GPS.altitude);
      Serial.print("Satellites: "); Serial.println((int)GPS.satellites);
    }
  }

  delay(1000);
  char radiopacket[2] = "N";
  Serial.println(radiopacket);
  radiopacket[1] = 0;
  rf95.send((uint8_t *)radiopacket, 2);
  rf95.waitPacketSent();

  delay(1000);
  char Lat[1] = {};
  dtostrf(La, 9, 4, Lat);
  Serial.print("Sending "); Serial.println(Lat);
  rf95.send((uint8_t *) &Lat, 9);
  delay(10);
  rf95.waitPacketSent();

  delay(1000);
  char Lon[1] = {};
  dtostrf(Lo, 9, 4, Lon);
  Serial.print("Sending "); Serial.println(Lon);
  rf95.send((uint8_t *) &Lon, 9);
  delay(10);
  rf95.waitPacketSent();

  delay(1000);
  char Spd[1] = {};
  dtostrf(Sp, 3, 0, Spd);
  Serial.print("Sending "); Serial.println(Spd);
  rf95.send((uint8_t *) &Spd, 3);
  delay(10);
  rf95.waitPacketSent();

  delay(1000);
  char Alt[1] = {};
  dtostrf(Al, 6, 0, Alt);
  Serial.print("Sending "); Serial.println(Alt);
  rf95.send((uint8_t *) &Alt, 6);
  delay(10);
  rf95.waitPacketSent();

  for (int i = 0; i <= f; i++) {

    String dataString = "";

    dataString += String(now.month()) += "/" ;
    dataString += String(now.day()) += "/";
    dataString += String(now.year()) += " ";
    dataString += String(now.hour()) += ":";
    dataString += String(now.minute()) += ":";
    dataString += String(now.second()) += " ";
    dataString += String(GPS.latitude, 4);
    dataString += String(GPS.lat) += ", ";
    dataString += String(GPS.longitude, 4);
    dataString += String(GPS.lon) += " ";
    dataString += String((int)GPS.satellites) += " Sats ";
    dataString += String(bmp280.readTemperature()) += "C MinI:";
    dataString += String(MinITem) += "C MaxI:";
    dataString += String(MaxITem) += "C MinO:";
    dataString += String(MinOTem) += "F MaxO:";
    dataString += String(MaxOTem) += "F Humidity:";
    dataString += String(sht30.readHumidity()) += "% MaxHum:";
    dataString += String(MaxHum) += "% ";
    dataString += String(bmp280.readPressure()) += "Pa BMP Max Alt ";
    dataString += String(MaxBMPAlt) += "m ";
    dataString += String(GPS.speed) += "kn ";
    dataString += String(MaxSpd) += "kn MAX ";
    dataString += String(GPS.altitude) += "m ";
    dataString += String(MaxAlt) += "m MAX MagX:";
    dataString += String(lis3mdl.x) += " MagY:";
    dataString += String(lis3mdl.y) += " MagZ:";
    dataString += String(lis3mdl.z) += " uTesla Acc x:";
    dataString += String(accel.acceleration.x) += " Acc y:";
    dataString += String(accel.acceleration.y) += " Acc z:";
    dataString += String(accel.acceleration.z) += " m/s^2 Gyro x:";
    dataString += String(gyro.gyro.x) += " Gyro y:";
    dataString += String(gyro.gyro.y) += " Gyro z:";
    dataString += String(gyro.gyro.z);

    File dataFile = SD.open("datalog.txt", FILE_WRITE); //Opens new text file on SD card called datalog.txt

    if (dataFile) {
      digitalWrite(LED_BUILTIN, HIGH);
      dataFile.println(dataString);
      dataFile.close();
      digitalWrite(LED_BUILTIN, LOW);
    }
    Serial.println(dataString);
  }
  if (MaxAlt > 4000 && GPS.altitude < 3000) {
    servo.write(15);
    f = 100;
  }

}

After you do the upload thingy, what is reported about memory?

What MCU are you using?

What do you get from the Dallas temper-thingy that the BMP280 does not supply? Getting rid of the dallas-thingy and the one wire library would free up some memories.

Have you commented out code sections to see what device is giving the issue?

-- ! --

1 Like

i wonder if it's a memory space issue considering what i've heard about String fragmentation.

it looks like there is a way to measure available memory, see freememory().

could be checked in setup() and monitored in loop(). see if it's continually shrinking.

That would be a corrupt stack.

looks like there are 4 other cases like that. suggests other problems

Idaho, it states that 34% of program storage space is used. I am using an Adafruit Feather M0 RFM96 LoRa Radio (433 MHz). The Dallas temperature thing measures the temperature outside of the case, while the BMP measures inside the case.

Coding, WOW! I can't believe I made such a rookie move lol. Thank you for post 3. As far as post 5, why is that a corrupt stack? I thought that was the proper way to use dtostrf to send info with LORA.

gcjr I will play with freememory() and report back. Thank you for that suggestion.

What size does Alt have to be?

This code will tell you what size Alt needs to be

//https://forum.arduino.cc/t/arduino-stops-after-a-few-hours/891064/7
// download SafeString V4.1.5+ library from the Arduino Library manager or from
// https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
#include "SafeString.h"
void setup() {
  Serial.begin(115200);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);  delay(500);
  }
  Serial.println();
  SafeString::setOutput(Serial);

  int Al = 355;//GPS.altitude;
  char Alt[1] = {};
  createSafeStringFromCharArray(sfAlt,Alt);
  sfAlt.clear(); 
  sfAlt.print(Al);
}
void loop() {
}

Output is

Error: sfAlt.print() needs capacity of 3(i.e. char[4])
        Input arg was '355'
        sfAlt cap:0 len:0 ''

why not just make them 20?

You have multiple arrays (radiopacket, Lat, Lon, Spd, Alt) which AFAICT are not reused. Instead you should declare a single array, large enough to do it all, and then reuse that array:

  char chbuf[20];

  delay(1000);
  chbuf[0] = 'N';
  chbuf[1] = 0;
  Serial.println(chbuf);
  rf95.send((uint8_t *)chbuf, 2);
  rf95.waitPacketSent();

  delay(1000);
  dtostrf(La, 9, 4, chbuf);
  Serial.print("Sending "); Serial.println(chbuf);
  rf95.send((uint8_t *) chbuf, 9);
  delay(10);
  rf95.waitPacketSent();

  delay(1000);
  dtostrf(Lo, 9, 4, chbuf);
  Serial.print("Sending "); Serial.println(chbuf);
  rf95.send((uint8_t *)chbuf, 9);
  delay(10);
  rf95.waitPacketSent();

  delay(1000);
  dtostrf(Sp, 3, 0, chbuf);
  Serial.print("Sending "); Serial.println(chbuf);
  rf95.send((uint8_t *) chbuf, 3);
  delay(10);
  rf95.waitPacketSent();

  delay(1000);
  dtostrf(Al, 6, 0, chbuf);
  Serial.print("Sending "); Serial.println(chbuf);
  rf95.send((uint8_t *)chbuf, 6);
  delay(10);
  rf95.waitPacketSent();

In fact, most of the above code blocks could be a method called multiple times instead of repeating similar code.

I fixed this and added a freememory() function as gcjr suggested. It ran through the night beautifully and returned 19735 for freememory() every time.

This is also another great suggestion and I will play around with that and report back, thank you for that.

did you check this before your fixes and see that memory was decreasing?

The original code with freememory() sprinkled in has been running for 8 hours now. The free memory dropped once from 19731 to 19447 and has remained there for about 7.5 hours. I figured it would have crashed by now. I'll leave it overnight for sure.