interrupts and RTC

I've been searching through this (and other) forums to find an answer, but couldn't find an answer to this problem.

Apparently something goes wrong when calling any rtc-command from within a ISR (Interrupt Service Routine).
I've also tried it using an I2C library for as well LCD and RTC, but have the same result.
I've also tried it using an Arduino Uno (with appropriate #includes and setup() ), but have the same result.
Anyone has a (simple! :slight_smile: ) sollution ? Tanx in advance !

Here's the relevant part of my code (for a Mega Arduino).

#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 14, 11, 7, 8, 9, 10);display
#include "DS3231.h"    // Real TIme clock + second pulse
DS3231  rtc(20, 21);


//////////////
// time variables
String TIME;
String DATE;
String Weekday;
int Hour;
int Minute;
int Second = 0;
byte Day;
byte Month;
int Year;
String timeDisplayString;

//////////////
// measuring variables
byte temp;

////////////////////////////////////////////////
void setup()
{
  Wire.begin();
  rtc.begin();
  rtc.setOutput(OUTPUT_SQW);
  rtc.setSQWRate(SQW_RATE_1); // output 1 second pulse
  lcd.begin(20, 4);
  // init interrupt :
  pinMode(13, OUTPUT);
  pinMode(18, INPUT_PULLUP);
  attachInterrupt(5, readinputs, FALLING); // set interupt 5 on pin18
} //END setup()

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

void readTime()
{
  TIME = rtc.getTimeStr(FORMAT_LONG);
  DATE = rtc.getDateStr(FORMAT_SHORT);
  Hour = TIME.substring(0, 3).toInt();
  Minute = TIME.substring(3, 5).toInt();
  Second = TIME.substring(6).toInt();
  Day = DATE.substring(0, 3).toInt();
  Month = DATE.substring(3, 5).toInt();
  Year = DATE.substring(6).toInt();
  Weekday = rtc.getDOWStr(FORMAT_SHORT);
  timeDisplayString =  (Weekday + " " + DATE + "   " + TIME);
} // END readTime()

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

void readTemp()
{
  temp = map(analogRead(A2), 0, 1023, 0, 99);  
} // END readTemp()


////////////////////////////////////////////////
//READ INPUTS - Interrupt Service Routine//
void readinputs()
{
  //readTime(); // ---------> when I uncomment this line, the program just stops
  readTemp();
}

////////////////////////////////////////////////
void loop()
{
   lcd.print(timeDisplayString);
   /*
   ...
   ...
   */
} // END loop()

(deleted)

PaulVdB:
Anyone has a (simple! :slight_smile: ) sollution ? Tanx in advance !

Yes: don't.
Seriously.

Interrupt service routines should be as short and as quick as possible. Reading and processing RTC output and doing analogRead is way more than advisable. The correct thing to do in your trivial use case is just to set a flag (or flags) in the ISR, eg. takeReadings = true;, and then check in loop() for the flag becoming true and do the readings from there. Since takeReadings is accessed from an ISR, it needs to be declared with the volatile modifier.

My simple solution avoids interrupts. Instead, I poll the seconds register about 10 times a second. You can do it more often if you need more accuracy. When the seconds register changes, I do my time stuff.

Thanks every one for you (SUPERFAST !!!) replies. I'll look into your answers and try to find the one that is most appropriate for me... (Although more solutions are always welcome :slight_smile: )
But first this :
I've read everywhere that an ISR should be as short as possible. But I have to say that (in another arduino project that controls a solar panel) I use a SUPER LONG ISR, and it works like a charm...
in this ISR :

  • I call several subroutines... (e.g. time reading from RTC and getting data over RS232)
  • I do 20 Analog readings...
  • I do some quite complex Floating Point calculations...
  • I write to a TFT display...
  • I even save data on a SD card while in this ISR...
  • etc...
    but I do NOT use an I2C (chain)...

and I just see that I trespass the 9000 character limit of this forum :slight_smile: (although MSWord only counts 8.825 characters ... ) so I'll reply with the second part in a 2nd reply ...

Am I just lucky ??

here's the code from this ISR : (I agree that this is not the most perfect code, but it works...)

////////////////////////////////////////////////////
//READ INPUTS - Interrupt Service Routine//
void readinputs()
{
  samplecounter ++;
  SD.remove("samples.txt");
  SAMPLES = SD.open("samples.txt", FILE_WRITE);
  SAMPLES.println(ltoa(samplecounter, sampleBuffer, 10));
  SAMPLES.close();

  myGLCD.setColor(0, 255, 0);          // green square bottom right ON
  myGLCD.fillRect(225, 305, 239, 319);

  readTracer();

  // Analog readings
  //////////////////
  Isolar = 0;
  Icharger = 0;
  for (n = 0; n < 10; n++)
  {
    Isolar = Isolar + analogRead(A7) - 512; // IchargeSolar           (0-10A)

  }
  Isolar = Isolar / 10;
  mapIsolar = map( Isolar * 2, 0, 1023, 0, 1000) / 40.0; // SOLAR PANEL CURRENT(0-10.0A)
  Serial.println();
  Serial.println(mapIsolar);
  Serial.println();
  if (mapIsolar < 0) mapIsolar = 0;

  for (n = 0; n < 10; n++)
  {
    Icharger = Icharger + analogRead(A8) - 512; // Icharge           (0-10A)
  }
  Icharger = Icharger / 10;
  mapIcharger = map( Icharger * 2, 0, 1023, 0, 1000) / 40.0; // SOLAR PANEL CURRENT(0-10.0A)
  if (mapIcharger < 0) mapIcharger = 0;

  Serial.println();
  Serial.println(mapIcharger);
  Serial.println();
/*
  if (ACcharging == 1)
  {
    mapIcharger = map(Icharger * 2, 0, 1023, -1000, 1000) / 40.0; // * stepdowncurrentadj;    // STEPDOWN CURRENT   (0-10.0A)
    if (mapIcharger < 0.05)mapIcharger = 0;
  }
  else mapIcharger = 0;
*/
  //overvoltage protection
  ////////////////////////
  if (mapUbat > maxbatvolt)
  {
    chargerstopdelay ++;
    if (chargerstopdelay > 5)
    {
      digitalWrite(11, LOW);
      ACcharging = 0;
      chargetime = 0;
      chargerstopdelay = 0;
    }
  }

  //KWH update
  ////////////
  KWH_solar = KWH_solar + (mapUpanel * mapIsolar / 3600000);
  KWH_net = KWH_net + (0 * mapIcharger);
  KWH_out = KWH_out + (mapUbat * mapIbat);

  readtime();
  // if (MINute % 6 == 0) {}
  if (SECond % 10 == 0)
  {
    myGLCD.fillRect(225, 305, 239, 319);
    myGLCD.setColor(255, 0, 0);
    myGLCD.fillRect(225, 305, 239, 319);
    savecounter ++;
    logwrite();
  }

  // CHARGER start/stop
  /////////////////////
  if (mapUbat < chargerstart)
  {
    chargetime = 0;
    chargerstartdelay ++;
    if (chargerstartdelay > 60)
    {
      digitalWrite(11, HIGH); // start charger
      ACcharging = 1;
      chargerstartdelay = 0;
    }
  }

  if (ACcharging == 1)
  {
    if (chargetime == 1) ACcount++;
    if (chargetime > chargeTimeSet)
    {
      digitalWrite(11, LOW); // stop charger
      chargetime = 0;
      ACcharging = 0;
      chargerstartdelay = 0;
    }
    chargetime++;
  }
  // END CHARGER start/stop

  myGLCD.setColor(0, 0, 0);
  myGLCD.fillRect(225, 305, 239, 319);
  displayTime();

  switch (menu)
  {
    case 0:
      myGLCD.printNumI(samplecounter, 0, 295, 10); 
      displayTime();
      break;
    case 1:
      OVERVIEW_DATA();
      displayTime();
      break;
    case 2:
      printdatamenu2();
      displayTime();
      break;
    case 3:
      displayTime();
      KWHdata();
      break;
    case 4:
      displayTime();
      break;
    case 5:
      displayTime();
      break;
    case 6:
      displayTime();
      break;
    case 7:
      displayTime();
      break;
    case 8:
      myGLCD.setColor(255, 255, 0);
      myGLCD.printNumI(samplecounter, 160, 192, 9);
      myGLCD.printNumI(savecounter, 160, 204, 9);
      if (samplecounter > 999) myGLCD.drawPixel(207, 202);
      if (samplecounter > 999999) myGLCD.drawPixel(182, 202);
      if (savecounter > 999) myGLCD.drawPixel(207, 214);
      if (savecounter > 999999) myGLCD.drawPixel(182, 214);
      break;
    case 9:
      displayTime();
      break;
    case 10:
      currentadjustmentsdata();
      break;
    case 11:
      displayTime();
      break;
    case 12:
      displayTime();
      break;
    case 13:
      myGLCD.setColor(255, 255, 255);
      myGLCD.printNumI(samplecounter, 0, 0);
      myGLCD.printNumF(mapIbat, 2, 188, 134, '.', 5, ' '); // charge current
      myGLCD.setColor(255, 255, 0);
      myGLCD.printNumF(mapUbat, 1, 40, 270, '.', 4, ' '); // battery voltage
      myGLCD.setColor(0, 255, 50);
      myGLCD.printNumF(mapIsolar , 2, 88, 122, '.', 5, ' '); // panel current
      myGLCD.setColor(255, 0, 50);
      myGLCD.printNumF(mapIcharger, 2, 88, 134, '.', 5, ' '); // charge current
      myGLCD.setColor(255, 100, 0);
      myGLCD.printNumI(chargetime, 88, 270, 4);  // charger ON time
      myGLCD.setColor(0, 255, 0);
      myGLCD.printNumF(mapUpanel, 1, 56, 280, '.', 4, ' ');
      // waitingtime (secs) before next sample
      myGLCD.setColor(255, 255, 255);
      myGLCD.printNumI((((samplecounter / interval) + 1) * interval) - samplecounter, 207, 271, 4);

      if (samplecounter % interval == 0)
      {
        if (ACcharging == 1)
        {
          myGLCD.setColor(255, 100, 0);
          myGLCD.drawLine(xx - 1, 260, xx - 1, 256);
          myGLCD.fillCircle(5, 302, 5);
          myGLCD.setColor(255, 255, 255);
        }
        if (ACcharging == 0)
        {
          myGLCD.setColor(255, 100, 0);
          myGLCD.drawLine(xx - 1, 260, xx - 1, 261);
          myGLCD.setColor(0, 0, 0);
          myGLCD.fillCircle(5, 302, 5);
          myGLCD.setColor(255, 255, 255);
        }

---> continues on next reply ... :slight_smile:

During an ISR, nothing else can run. So if it runs for too long a period, it kills any multitasking that you are doing. Any time sensitive tasks fail because they don't get serviced.

Second part : (hmmm... I have to wait 5 minutes to post the rest ... :slight_smile: )

        ////////////
        //GRAPHICS//
        ////////////
        // battery voltage
        myGLCD.setColor(255, 255, 0);
        //myGLCD.printNumF(mapUbat, 1, 40, 270, '.', 4, ' ');
        myGLCD.drawLine(xx - 1, 267 - (mapUbatold * 4), xx, 267 - (mapUbat * 4));
        mapUbatold = mapUbat;

        // panel current
        myGLCD.setColor(0, 255, 50);
        // myGLCD.printNumF(mapIsolar , 2, 88, 122, '.', 5, ' ');
        myGLCD.drawLine(xx - 1, 120 - (mapIsolarold * 20), xx, 120 - (mapIsolar * 20));
        mapIsolarold = mapIsolar;

        // charge current
        myGLCD.setColor(255, 0, 50);
        myGLCD.printNumF(mapIcharger, 2, 88, 134, '.', 5, ' ');
        myGLCD.drawLine(xx - 1, 120 - (mapIchargerold * 20), xx, 120 - (mapIcharger * 20));
        mapIchargerold = mapIcharger;

        // battery current
        myGLCD.setColor(255, 255, 255);
        myGLCD.drawLine(xx - 1, 60 - (mapIbatold * 5), xx, 60 - (mapIbat * 5));
        mapIbatold = mapIbat;

        // panel voltage
        myGLCD.setColor(0, 255, 0);
        myGLCD.drawLine(xx - 1, 267 - (mapUpanelold * 1.5), xx, 267 - (mapUpanel * 1.5));
        mapUpanelold = mapUpanel;
        ////////////////
        //END GRAPHICS//
        ////////////////

        totIsolar = totIsolar + mapIsolar;
        avgIsolar = totIsolar / xx;
        totIcharger = totIcharger + mapIcharger;
        avgIcharger = totIcharger / xx;
        myGLCD.setColor(255, 255, 255);

        xx++;
      }

      if (xx > 239)
      {
        tone(12, 2000);
        totIsolar = totIcharger = avgIsolar = avgIcharger = 0;
        resetxyn();
        myGLCD.setColor(0, 0, 0);
        myGLCD.fillRect(1, 146, 239, 267);
        myGLCD.fillRect(1, 0, 239, 120);

        myGLCD.setColor(60, 60, 60);
        for (yy = 0; yy < 120; yy = yy + 10)
        {
          myGLCD.drawLine(1, yy, 239, yy);
          myGLCD.drawLine(1, yy + 147, 239, yy + 147);
        }
        for (xx = 60; xx < 240; xx = xx + 60)
        {
          myGLCD.drawLine(xx, 0, xx, 120);
          myGLCD.drawLine(xx, 147, xx, 267);
        }
        myGLCD.setColor(200, 200, 0);
        myGLCD.drawLine(1, 60, 239, 60);
        myGLCD.setColor(60, 60, 60);

        xx = 0;
        myGLCD.setColor(255, 150, 200);
        myGLCD.print("6", 228, 0);
        myGLCD.print("5", 228, 15);
        myGLCD.print("4", 228, 34);
        myGLCD.print("3", 228, 53);
        myGLCD.print("2", 228, 73);
        myGLCD.print("1", 228, 93);
        myGLCD.print("A", 228, 106);
        myGLCD.setColor(255, 150, 200);
        myGLCD.print("12", 220, 148);
        myGLCD.print("10", 220, 163);
        myGLCD.print("8", 228, 182);
        myGLCD.print("6", 228, 201);
        myGLCD.print("4", 228, 221);
        myGLCD.print("2", 228, 241);
        myGLCD.print("V", 228, 254);
        noTone(12);
      }
      break;
    case 14:
  }
  noTone(12);
} //END readinputs

aarg:
During an ISR, nothing else can run. So if it runs for too long a period, it kills any multitasking that you are doing. Any time sensitive tasks fail because they don't get serviced.

That's OK for me... I use the "1 second pulses" from the RTC to trigger my ISR. If you look to the code carefully, you'll see that at the begin of the ISR, I light up a green square at the bottom of my TFT, and when the ISR is finished, I switch that green square off. I can tell you that I really have to look closely to see that square light up ... I guess that I have 50 times more time before the next Interrupt occurs...

Nevertheless : thanks for your interest and your time !

And also : I didn't use ANY "volatile" variable in that long ISR ... (when I wrote this sketch, I didn't know to use volatile variables (yet) 8) ...

I did try to make all the variables (in the ISR) in my problem-sketch "volatile", but still the same problem...

I'm confused - your problem is solved?

aarg:
I'm confused - your problem is solved?

I hope 5 mins have passed ...

LOL ... nono ... problem not solved. :slight_smile:
I think I'm gonna try the solution that "spycatcher2k" and "arduarn" proposed...
Only thing : I don't see immediately how to do this ... (this is a "level UP" compared to my knowledge ...)

aarg:
Gammon Forum : Electronics : Microprocessors : Interrupts

Thanks, aarg,

I've been there before I posted on this forum... It looked quite "difficult" for an "amateur-programmer" like me, but I guess I'll have to fight my way through this document... :frowning: I thought there might have been an easier solution :frowning:
Nevertheless : thanks for your help, If I find a solution myself, I'll post it here so our arduino community can benefit from it.
but if you might have the time to dig up other solutions : more than welcome ! :wink:

PaulVdB:
I've been searching through this (and other) forums to find an answer, but couldn't find an answer to this problem.

Apparently something goes wrong when calling any rtc-command from within a ISR (Interrupt Service Routine).

You are using String methods both in the ISR and in loop - String is not ISR-safe so cannot be used in
an ISR unless you lock out all String-calls in the main program with critical sections.

PaulVdB:
I thought there might have been an easier solution :frowning:

There is. Reply #3. There is no need for interrupts in this application. There is no magic bullet that will make interrupts simple.

Sorry for replying so late. After trying to do more with my interrupt, I decided to use the interrupt only to set a flag and use this flag in my main loop.
But ... 8'm still wondering why in my other project the "mega-long" ISR works like a charm, while using exactly the same Arduino, same brand, same model... (Mega 2560 R3)

Nevertheless : Thanx to everyone who joined and helped me with this issue. If you ever are in Belgium : lemme know and I'll buy you a (some) beer(s) ! :slight_smile: