Code crash after 5-100 sec. Only on Micro, not on Mega.

Hi, my code crashes after 5-100 sec. when using a Arduino Micro. It worked fine on a Mega 2560 R3. The crash manifests as weird characters scrolling over the LCD. Only minor modifications done to use interrupt on the Micro from the Mega code.

It’s a radiation monitor, and the attachinterrupt() has 0 - 15 events per second.

What is going on? Tried to monitor memory usage, but it was stable.

#include <Wire.h>
#include <LCD.h> // I can remove this
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR    0x27

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

volatile int CNT;
volatile int CPS;

unsigned long dispPeriod;
const int numReadings = 59; //sekunder gjennomsnittet måles over, 0 er med så 59 gir 60 sekunder

double readings[numReadings];
int index = 0;
int slett = 1;
double total = 0;

void setup()
{
  Serial.begin(115200);
  lcd.begin(16, 2);
  lcd.setBacklight(HIGH);
  attachInterrupt(4,GetEvent,FALLING); //switched to 4 from 0
                
  for (int thisReading = 0; thisReading < numReadings; thisReading++){
    readings[thisReading] = 0;
  }
  warmup();
}

void loop() {
  
  if (millis() >= dispPeriod) {
    dispPeriod = millis() + 1000;
    readings[index] = CNT;
    CPS = CNT;
    CNT = 0;  
    total = total + readings[index] - readings[slett];
    index++;
    slett++; 

    if (index >= numReadings) {             
      index = 0;
      }

    if (slett >= numReadings) {
       slett = 0; 
       }
    
       running();
  }
}

void GetEvent(){
  CNT++;
}

void running(){
      lcd.clear();
      lcd.print(total * (0.0057 / 4)); //konverteringsfaktor mellom cpm og µSv/h, delt på antall rør (4)
      lcd.print(" ");
      lcd.print(char(228));
      lcd.print("Sv/h");
      lcd.setCursor(0,1);
      lcd.print(total,0);
      lcd.print(" cpm ");
      lcd.print(CPS);
      lcd.print(" cps");
      CPS = 0;
}

void warmup(){ // Delay to collect the 60 first CPS in the array.
    lcd.home();
    lcd.print("Warming Up");  
    while (millis() < 60000){
      }
}

my code crashes after 5-100 sec.

You may want to look at decoupling the hardware a bit more.

Since the CNT variable is bigger than a byte, it would be better to disable interrupts, copy the value to a local temporary variable (and reset the value if you want) and then enable interrupts again, before going on to to the logic below:

    readings[index] = CNT;
    CPS = CNT;
    CNT = 0;

It hardly matters in this case, but this code:

dispPeriod = millis() + 1000;

would be better as:

dispPeriod += 1000;

The modified version keeps accurate time even if there is a delay between millis() ticking over to the new value, and your code detecting that. The name dispPeriod is a bit misleading; the variable dispPeriod represents the last display time not the period (the period is 1000).

You could eliminate the variable slett and use '(index + 1) % numReadings' instead.

None of this explains why you're getting display corruption. Can you add some debug output (blinky LED, or serial port debug output) to confirm the sketch is still running? How well conditioned is your power supply, and what scope is there for electrical noise to enter the Arduino via connected leads?

Hi, thanks for fast and useful responses. I will try your suggestions. And I have a few more questions:

LarryD:
You may want to look at decoupling the hardware a bit more.

What do you suggest I do?

PeterH:
Since the CNT variable is bigger than a byte, it would be better to disable interrupts, copy the value to a local temporary variable (and reset the value if you want) and then enable interrupts again, before going on to to the logic below:

    readings[index] = CNT;

CPS = CNT;
   CNT = 0;



Would I miss any interrupts during the disabled period?

It hardly matters in this case, but this code:


dispPeriod = millis() + 1000;




would be better as:


dispPeriod += 1000;



Done, thanks. Always looking to optimise my code, but I'm still an amateur.

You could eliminate the variable slett and use '(index + 1) % numReadings' instead.
I thought of this, but then I couldn't figure out how to reset "slett" to 0 before "index" is reset.

None of this explains why you're getting display corruption. Can you add some debug output (blinky LED, or serial port debug output) to confirm the sketch is still running? How well conditioned is your power supply, and what scope is there for electrical noise to enter the Arduino via connected leads?
It fails with both a fresh 9V battery and from USB as well. Will try some serial connection and see if it's the LCD or the code that corrupts. Good suggestion.

Add .1uf capacitors at the power pins of all components drawing power (example your LCD).
Would you show us a picture of your circuit?

PeterH:
Since the CNT variable is bigger than a byte, it would be better to disable interrupts, copy the value to a local temporary variable (and reset the value if you want) and then enable interrupts again

AVR libc also has some means for atomic access:

http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html

LarryD:
Add .1uf capacitors at the power pins of all components drawing power (example your LCD).
Would you show us a picture of your circuit?

Ok.

My LCD has no backlight enabled, connected to SCL (3), SDA (2), VCC (5V) and GND.

My radiation board is connected to VCC (5V), GND and INT (7).

Either VI (9V) and GND or USB is connected. Using USB now for debugging.

I added some serial code now, Serial.println(CPS); and it still reports the CPS after the LCD fails. So the code runs, but the LCD output is corrupted.

Would I miss any interrupts during the disabled period?

No - the chip will latch the first interrupt (of each type) while interrupts are disabled - you would only miss an interrupt if you kept them disabled for so long that TWO interrupts occured.

I thought of this, but then I couldn't figure out how to reset "slett" to 0 before "index" is reset.

You don't need to; the slett variable would cease to exist. The expression I gave you correctly deals with the counter reaching the end of the array.

PeterH:
You don't need to; the slett variable would cease to exist. The expression I gave you correctly deals with the counter reaching the end of the array.

I tried to read about %, but couldn't understand how this correctly keeps the variable ONE step ahead of index.

'index = 0' then '(index + 1) % numreadings' //should be 1, is it?

and

'index = 58' then '(index + 1) % numreadings' //should be 59, is it?

and

'index = 59' then '(index + 1) % numreadings' //should be 0, is it?

If I put lcd.init(); in the loop the problem disappears. But then the code is too slow. I found one 1.0 µF capacitor, but over the LCD i didn't work. Nor over the radiation board. I'm testing over the 5v rail on the Arduino micro now.