Sketch stops working after a few hours

Hi,

I am using Atmega328P board with Arduino Nano bootloader. I have attached below my full code. No compilation errors. After reset, the sketch works as expected. After the passage of a few hours, it seems that it's not working anymore. I am using DS3231 RTC. The sketch mainly enters sleep mode for 8 seconds and wakes up to check the hour. If the hour equals a specific time, it executes a specific action. Please let me know what is the problem with the code. "If" statements seems to not work after a few hours.

[code]
//Libraries
#include <DHT.h>        // Adafruit DHT Library
#include <DS3232RTC.h>  //RTC Library https://github.com/JChristensen/DS3232RTC
#include "HX711.h"
#include <SoftwareSerial.h>
#include <OneWire.h> //Wire temp sensor
#include <DallasTemperature.h> //Wire temp sensor
#include "Timer.h"
#include "LowPower.h"

//Software serial
SoftwareSerial mySerial(8, 7);

// Settings for measuring battery level
float adc_voltage = 0.0;
float in_voltage = 0.0;
float R1 = 2589.0;
float R2 = 3000.0;
float ref_voltage = 5.0;
int adc_value = 0;

//TinyGSM configuation
#define TINY_GSM_MODEM_SIM808
#include <TinyGsmClient.h>
TinyGsm modem(mySerial);

//Interrupt pin
#define TILT 3

//DHT setup
#define DHTPIN 10
#define DHTTYPE DHT21
DHT dht(DHTPIN, DHTTYPE);

//Brood temp setup
#define ONE_WIRE_BUS 11
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
float Celcius = 0;

//Siren setup
#define SIREN 12

//HX711 setup
#define calibration_factor -23550.0
#define LOADCELL_DOUT_PIN  5
#define LOADCELL_SCK_PIN  6
HX711 scale;



//SIM800 POWER ON
#define SIMSWITCH 9
//#define PWR 4 //disable if PWR connected to GND

//Setup battery level pins
#define MAINBAT A1
#define MAINBATSW A0

//flag for tilt interrupt
volatile bool emergency = false;

//counter set for 4 times a time recording of variables
int data8am = 0;
int data12am = 0;
int data4pm = 0;
int data8pm = 0;


void setup() {
  pinMode(TILT, INPUT);
  pinMode(SIMSWITCH, OUTPUT);
  pinMode(SIREN, OUTPUT);
  pinMode(MAINBAT, INPUT);
  pinMode(MAINBATSW, OUTPUT);

  //Start all temperature sensors and loadcell
  dht.begin();//Start the DHT sensor
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale(calibration_factor);
  sensors.begin(); //Start onwire library

  setSyncProvider(RTC.get); //function to get time from RTC

  Serial.begin(9600);
  mySerial.begin(9600);
  TURNONSIM();
  delay(1000);
  mySerial.println("AT+CMGF=1");
  delay(500);
  updateSerial();
  mySerial.println("AT+CMGS=\"+............................\"");
  delay(500);
  updateSerial();
  mySerial.print("Beescale");
  delay(500);
  updateSerial();
  mySerial.write(26);
  delay(3000);
  TURNOFFSIM();
  scale.power_down();
  delay(1000);
  attachInterrupt(digitalPinToInterrupt(3), EMERGENCY, CHANGE);//attaching a interrupt to TILT sensor
}

void loop() {
  Serial.flush();
  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);


  //checks for measurements trigger 4 times a day, 8am, 12pm, 4pm, 8pm
  if (hour() == 8 && data8am == 0 ) {
    data8am++;
    MEASUREMENTS_FINAL();
  }

  if (hour() == 12 && data12am == 0) {
    data12am++;
    MEASUREMENTS_FINAL();
  }

  if (hour() == 23 && data4pm == 0) {
    data4pm++;
    MEASUREMENTS_FINAL();
  }

  if (hour() == 20 && data8pm == 0) {
    data8pm++;
    MEASUREMENTS_FINAL();
  }

  //reset timers

  if (hour() == 9) {
    data8am = 0;
  }

  if (hour() == 13) {
    data12am = 0;
  }

  if (hour() == 17) {
    data4pm = 0;
  }

  if (hour() == 21) {
    data8pm = 0;
  }

  if (emergency) {
    emergency = false;
    Serial.begin(9600);
    Serial.println("Alarm");
  }
}


void MEASUREMENTS_FINAL() {
  Battery();
  float h = dht.readHumidity();
  delay(500);
  float t = dht.readTemperature();
  delay(500);
  sensors.requestTemperatures();
  delay(500);
  float ht = Celcius = sensors.getTempCByIndex(0);
  delay(500);
  scale.power_up();
  delay(500);
  float w = scale.get_units();
  delay(500);
  scale.power_down();
  delay(500);
  TURNONSIM();
  Serial.begin(9600); //delete on final version
  mySerial.begin(9600);
  updateSerial();
  mySerial.println("AT+CMGF=1");
  delay(1000);
  updateSerial();
  mySerial.println("AT+CMGS=\"+....................\"");
  delay(1000);
  updateSerial();
  mySerial.print("Weight:");
  mySerial.print(w);
  mySerial.print("kg");
  mySerial.println("Amb:");
  mySerial.print(t);
  mySerial.println("Hum:");
  mySerial.print(h);
  mySerial.println("HTemp:");
  mySerial.print(ht);
  mySerial.println("Battery:");
  mySerial.print(in_voltage, 2);
  mySerial.write(26);
  delay(3000);
  TURNOFFSIM();
}

void EMERGENCY() {
  detachInterrupt(digitalPinToInterrupt(3));
  emergency = true;
  attachInterrupt(digitalPinToInterrupt(3), EMERGENCY, CHANGE);//attaching a interrupt to TILT sensor
}

void TURNONSIM() {
  digitalWrite(SIMSWITCH, HIGH);
  delay(2000);
  if (!modem.waitForNetwork()) {
    Serial.println(" fail");
    delay(5000);
    return;
  }
}

void TURNOFFSIM() {
  delay(1000);
  digitalWrite(SIMSWITCH, LOW);
}


//sends software serial data to arduino serial
void updateSerial()
{
  delay(500);
  while (Serial.available())
  {
    mySerial.write(Serial.read());//Forward what Serial received to Software Serial Port
  }
  while (mySerial.available())
  {
    Serial.write(mySerial.read());//Forward what Software Serial received to Serial Port
  }
}

//Battery reading

void Battery() {

  digitalWrite(MAINBATSW, HIGH);
  delay(500);

  adc_value = analogRead(MAINBAT);

  // Determine voltage at ADC input
  adc_voltage  = (adc_value * ref_voltage) / 1024.0;

  // Calculate voltage at divider input
  in_voltage = adc_voltage / (R2 / (R1 + R2));

  digitalWrite(MAINBATSW, LOW);
  delay(500);

}


[/code]

Do I get right you are using a custom board? have you checked it for stability in general?

you might want to wait a bit of time after wakeup so that everything is initialized correctly.

side notes:

  • why don't you use the alarm feature of the DS3231 to get an interrupt that will wake you up only at the right time?

  • why do you detach and re-attach the ISR for EMERGENCY? Should CHANGE be the right mode? (if that a button you press?)

Yes i am using a custom board. I have checked it for stability. Everything works fine and voltage is ok. I think the problem has to do with DS3231. I believe it stops providing the time to the IC after some time. I should be missing something in the code.

I need 4 alarms and DS3231 has only 2. I though about it but i would need to change alarm on every wake event.

Is it fine if i attach only once the interrupt? Do i need to detach interrupt after triggering?

Do you have pull-ups for your I2C lines?

You really need only one indeed, and when you wake up you set the alarm for the next time.
Adafruit's RTCLib will make it easy to configure the RTC's alarm.

Is it fine if i attach only once the interrupt? Do i need to detach interrupt after triggering?

Yes, attach it in the setup, never detach it, just set the flag to true. If it's a button I would configure the pin as INPUT_PULLUP. Going low would mean you pressed the button, no external hardware needed besides may be a cap to prevent bouncing (trigger on change, check the pin status in the ISR to set the flag to true).

If you suspect the RTC, then I think you could temporarily use multiple 8 second sleeps to simulate the passage of time. It's not particularly accurate but it would help to eliminate an RTC issue.

Your issue may be electrical, but the following slab of code hurts my eyes:

if (hour() == 8 && data8am == 0 ) {
    data8am++;
    MEASUREMENTS_FINAL();
  }

  if (hour() == 12 && data12am == 0) {
    data12am++;
    MEASUREMENTS_FINAL();
  }

  if (hour() == 23 && data4pm == 0) {
    data4pm++;
    MEASUREMENTS_FINAL();
  }

  if (hour() == 20 && data8pm == 0) {
    data8pm++;
    MEASUREMENTS_FINAL();
  }

  //reset timers

  if (hour() == 9) {
    data8am = 0;
  }

  if (hour() == 13) {
    data12am = 0;
  }

  if (hour() == 17) {
    data4pm = 0;
  }

  if (hour() == 21) {
    data8pm = 0;
  }

Try to replace it with this much simpler approach:

//Global declarations:
const uint8_t MEASURE_HOURS[] = { 8, 12, 20, 23 };
uint8_t last_measure = 0;

//Loop code
int hr = hour();
if (hr != last_measure)
{
  for (uint8_t i = 0; i < sizeof(MEASURE_HOURS); i++)
  {
    if (hr == MEASURE_HOURS[i])
    {
      last_measure = hr;
      MEASUREMENTS_FINAL();
      break;
    }
  }
}

All your "data**m" integers are no longer used.

yeah. Also there is not point checking the other times if you found one.. so using else would make sense (or break in the case of your for loop)

Yes, it could also have been written as:

int hr = hour();
if ( (hr != last_measure) && ((hr == 8) || (hr == 12) || (hr == 20) || (hr == 23)) )
{
  last_measure = hr;
  MEASUREMENTS_FINAL();
}

That may even be better than the for loop.

[quote="J-M-L, post:4, topic:911255, full:true"]
Do you have pull-ups for your I2C lines?

Yes all I2C lines are pulled up using 10k resistor.

Hi guys,

I found the problem. I moved the following code to the loop section.

setSyncProvider(RTC.get); //function to get time from RTC

Thank you for the code. I will try it out.

Rather use the code from #8, simpler and sleeker! :wink:

I have tried the code and it works fine.

However i have another problem. Every time the hour matches the time set, it seems that the arduino resets itself and executes the setup section again. Is it something with the watchdog timer? I don't know.

If the arduino is actually resetting, the call to "MEASUREMENTS_FINAL()" would repeat itself over and over with few seconds in between until the hour does not match anymore. Is that what you experience?

No. This is not the case. As per the code, the call to MEASUREMENTS_FINAL is called once since the IF statement is true only once. I confirmed this with a multimeter. When the IF statement is true instead of performing MEASUREMENTS_FINAL code it also executes the setup section at first. I don't know why.

that's the test

if (hour() == 8 && data8am == 0 ) {
    data8am++;
    MEASUREMENTS_FINAL();
  }

what he meant (I think) was that if the arduino resets, then memory is erased (data8am will be 0) and since it's still the same time (it's 8am for a whole 1 hour), the if condition would still be true and thus it would trigger MEASUREMENTS_FINAL() again and again for one hour...

if you don't see that then the arduino gets stuck somewhere

A lot of the code in "setup()" seems more or less identical to the code in "MEASUREMENTS_FINAL()". Are you sure that "setup()" is re-executed? Try in setup and set an unused pin to HIGH for a second and then to LOW again. If you can measure that behaviour (with a LED or DMM) more than once, that would be quite odd.

If that was the case i would receive a lot of sms during one hour. However, i receive only 1 which is correct. The problem is that i also receive the SMS in the setup section every time MEASUREMENTS_FINAL triggers.

Yes i am sure since every time MEASUREMENTS_FINAL triggers, i also receive an sms with content "Beescale" as per setup section.

add a way to see you are going through setup() (led, Serial print, ...)

Do you get 2 SMS one with measurements and one with "Beescale" for every of the hours it should measure?