Timer problem?!

I’m having a problem with my application.

The application is as follows:
a thermostat device that switches on a heater at a certain minimum value and switches it off at an other value.

The application is quite straight forward:

So far so good. I’m a programmer, so combining some examples with my own code was no problem.
What is a problem is that I don’t want the heater to switch on and off too fast.

long lastTLswitch = 0;

// in the main loop:

if ((tlflag == 0) && ( millis() > (lastTLswitch + 2000))) {
  // switch on the heater
} else if (millis() > (lastTLswitch + 2000)) {
  // switch off the heater
}

The problem:
1376277 is the value in lastTLswitch even when I initialize it so it functions but with a timeout that is too long.
If I print millis() it even freezes.
I suspect that interrupt ISR(TIMER2_OVF_vect) causes this problem.

I have looked around, but I didn’t find the answer yet.

And you're not going to find an answer here until you post your entire Sketch.

No problem :slight_smile: forum is picky about 9500 chars so I took out some irrelevant code (methods are there).

#include <nokia_3310_lcd.h>

// Temp/humidity display using nokia 3310 LCD display shield from nuelectronics.com
// LCD Code based on source from mr. Greenbeast.

int pin = 1; // analog pin
volatile int tempref = 0;
int tempf=0; // temperature variables
int samples[8]; // variables to make a better precision
int maxi = -100,mini = 100; // to start max/min temperature

long lastTLswitch = 0;

#define TL_MAXTMP 28.0
#define TL_MINTMP 25.0

//keypad debounce parameter
#define DEBOUNCE_MAX 15
#define DEBOUNCE_ON  10
#define DEBOUNCE_OFF 3 

#define NUM_KEYS 5

#define NUM_MENU_ITEM	5

// joystick number
#define UP_KEY 3
#define LEFT_KEY 0
#define CENTER_KEY 1
#define DOWN_KEY 2
#define RIGHT_KEY 4

// Pin used by Backlight, so we can turn it on and off. Pin setup in LCD init function
#define BL_PIN 7

// Pin used by Tubelight, so we can turn it on and off. Pin setup in LCD init function
#define TL_PIN 2

// menu starting points
#define MENU_X	10		// 0-83
#define MENU_Y	0		// 0-5

// adc preset value, represent top value,incl. noise & margin,that the adc reads, when a key is pressed
// set noise & margin = 30 (0.15V@5V)
int  adc_key_val[5] ={
  30, 150, 360, 535, 760 };

// debounce counters
byte button_count[NUM_KEYS];
// button status - pressed/released
byte button_status[NUM_KEYS];
// button on flags for user program 
byte button_flag[NUM_KEYS];

// menu definition
char menu_items[NUM_MENU_ITEM][12]={
  "TEMPERATURE",
  "HUMIDITY",
  "BACKLIGHT",
  "TUBELIGHT",
  "ABOUT"	
};

void (*menu_funcs[NUM_MENU_ITEM])(void) = {
  temperature,
  humidity,
  backlight,
  tubelight,
  about
};

char current_menu_item;
int blflag = 1;  // Backlight initially ON
volatile int tlflag = 0;  // Backlight initially OFF
char degree = 0x7b;  // Degree symbol


Nokia_3310_lcd lcd=Nokia_3310_lcd();

int getTemp0();

void setup()
{

  Serial.begin(9600); // start serial communication
  // setup interrupt-driven keypad arrays  
  // reset button arrays
  for(byte i=0; i<NUM_KEYS; i++){
    button_count[i]=0;
    button_status[i]=0;
    button_flag[i]=0;
  }

  // Setup timer2 -- Prescaler/256
  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
  TCCR2B &= ~(1<<WGM22);
  TCCR2B = (1<<CS22)|(1<<CS21);      

  ASSR |=(0<<AS2);

  // Use normal mode  
  TCCR2A =0;    
  //Timer2 Overflow Interrupt Enable  
  TIMSK2 |= (0<<OCIE2A);
  TCNT2=0x6;  // counting starts from 6;  
  TIMSK2 = (1<<TOIE2);    

  SREG|=1<<SREG_I;

  lcd.init();
  lcd.clear();

  //menu initialization
  init_MENU();
  current_menu_item = 0;
  lastTLswitch = millis();
  Serial.println(lastTLswitch);
}

void loop() {
  byte i;
  for(i=0; i<NUM_KEYS; i++) {
    if(button_flag[i] !=0) {

      button_flag[i]=0;  // reset button flag
      switch(i){
      case UP_KEY:
        // current item to normal display
        lcd.writeString(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
        current_menu_item -=1;
        if(current_menu_item <0)  current_menu_item = NUM_MENU_ITEM -1;
        // next item to highlight display
        lcd.writeString(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
        break;
      case DOWN_KEY:
        // current item to normal display
        lcd.writeString(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_NORMAL );
        current_menu_item +=1;
        if(current_menu_item >(NUM_MENU_ITEM-1))  current_menu_item = 0;
        // next item to highlight display
        lcd.writeString(MENU_X, MENU_Y + current_menu_item, menu_items[current_menu_item], MENU_HIGHLIGHT );
        break;
      case LEFT_KEY:
        init_MENU();
        current_menu_item = 0;
        break;   
      case RIGHT_KEY:
        lcd.clear();
        (*menu_funcs[current_menu_item])();
        lcd.clear();
        init_MENU();
        current_menu_item = 0;           
        break;	
      }
    }
  }
}

/* menu functions */
void init_MENU(void) {

  byte i;
  lcd.clear();
  lcd.writeString(MENU_X, MENU_Y, menu_items[0], MENU_HIGHLIGHT );

  for (i=1; i<NUM_MENU_ITEM; i++) {
    lcd.writeString(MENU_X, MENU_Y+i, menu_items[i], MENU_NORMAL);
  }
}

// waiting for center key press
void waitfor_OKkey() {
  byte i;
  byte key = 0xFF;
  while (key!= CENTER_KEY){
    for(i=0; i<NUM_KEYS; i++){
      if(button_flag[i] !=0){
        button_flag[i]=0;  // reset button flag
        if(i== CENTER_KEY) key=CENTER_KEY;
      }
    }
  }
}

// Check if joystick is moved or pressed
byte checkKeypressed() {
  byte key = 0xFF;

  for(int i=0; i<NUM_KEYS; i++){
    if(button_flag[i] !=0){
      button_flag[i]=0;  // reset button flag
      if(i== CENTER_KEY) key=CENTER_KEY;
    }
  }
  return key;
}

char numStr[8];

void formatNum( int num ) {
// Format a number to 2 decimal places
}

// Display temperature in big digits, humidity in small digits underneath
void temperature() {  
  int temp, humid;
  int tempc2;
  tempc2 = getTemp0();
  // Display non changing text, there is a slight delay while first reading is taken
  lcd.gotoXY( 62,1 );
  lcd.print( tempc2 );
  lcd.print( "C" );
  // lcd.writeString(67, 1, "C", MENU_NORMAL);
  lcd.writeString(38, 5, "OK", MENU_HIGHLIGHT );
  lcd.writeString(40, 3, "%RH", MENU_NORMAL);

  long lastUpdate = 0;  // Force update
  byte i;
  byte key = 0xFF;

  // Loop to display temperaure/humidity with check for key press to exit
  while (key!= CENTER_KEY) {
    // Update temp
    if( millis() > lastUpdate + 1000) {
      // Read temperature and humidity

      temp = (int) tempc2 * 100; 
      humid = 10; 

      // Display temp
      formatNum( temp );
      lcd.writeStringBig(8, 0, numStr, MENU_NORMAL);

      // Display Humidity
      formatNum( humid );
      lcd.writeString(10, 3, numStr, MENU_NORMAL);

      lastUpdate = millis();
      tempc2 = getTemp0();

      //      Serial.print("de temp is hier: ");
      //      Serial.println(tempc2);
    }
    key = checkKeypressed();

  }
}

// Display humidity in big digits, temperature in small digits underneath
void humidity() {
  int temp, humid;
  // show humidity, don't have that sensor in yet.
}

// Allow backlight to be turned on and off
void backlight() {

  lcd.writeString( 0, 1, "Toggle", MENU_NORMAL);
  lcd.writeString( 0, 2, "Backlight", MENU_NORMAL);
  if( blflag != 0 ) {
    lcd.writeString( 60, 2, "Off", MENU_HIGHLIGHT);
  } 
  else {
    lcd.writeString( 60, 2, "On", MENU_HIGHLIGHT);
  }

  waitfor_OKkey();

  if( blflag != 0 ) {
    blflag=0;
    digitalWrite( BL_PIN, LOW );
  }   
  else {
    blflag = 1;
    digitalWrite( BL_PIN, HIGH );
  }
}

// Allow tubelight to be turned on and off
void tubelight() {

  lcd.writeString( 0, 1, "Toggle", MENU_NORMAL);
  lcd.writeString( 0, 2, "TL Tube", MENU_NORMAL);
  if( tlflag != 0 ) {
    lcd.writeString( 60, 2, "Off", MENU_HIGHLIGHT);
  } 
  else {
    lcd.writeString( 60, 2, "On", MENU_HIGHLIGHT);
  }

  waitfor_OKkey();

  if( tlflag != 0 ) {
    tlflag=0;
    digitalWrite( TL_PIN, LOW );
  }   
  else {
    tlflag = 1;
    digitalWrite( TL_PIN, HIGH );
  }
}

// The followinging are interrupt-driven keypad reading functions
//  which includes DEBOUNCE ON/OFF mechanism, and continuous pressing detection
// Convert ADC value to key number

char get_key(unsigned int input) {
  char k;

  for (k = 0; k < NUM_KEYS; k++) {
    if (input < adc_key_val[k]) {
      return k;
    }
  }

  if (k >= NUM_KEYS)
    k = -1;     // No valid key pressed
  return k;
}

void update_adc_key() {
  // ... some code
}

int getTemp0() {
  
  int tempc = 0;
  cli(); 
  for(int i = 0;i<=15;i++){ // gets 8 samples of temperature
    samples[i] = ( 5.0 * analogRead(pin) * 100.0) / 1024.0;
    tempc = tempc + samples[i];
    delay(50);
  }

  tempc = tempc/16.0; // better precision
  tempf = (tempc * 9)/ 5 + 32; // converts to fahrenheit
  
  if (tempc < (TL_MAXTMP)) {
    if (tempc < TL_MINTMP) {
      Serial.println(tlflag);
      Serial.print("Vorige keer: ");
      Serial.println(lastTLswitch);
      Serial.println(millis()); // this makes the code even freeze!
      if ((tlflag == 0) && ( millis() > (lastTLswitch + 2000))) {
        Serial.println("Heater on...");
        tlflag = 1;
        digitalWrite( TL_PIN, HIGH );
        lastTLswitch = millis();
      }
    }
  } else {
    // if heater is off, leave off.... 
    if ((tlflag == 1) && ( millis() > (lastTLswitch + 2000))) {
      tlflag = 0;
      digitalWrite( TL_PIN, LOW );
      lastTLswitch = millis();
    }
  }

  if(tempc > maxi) {
    maxi = tempc;
  } // set max temperature
  if(tempc < mini) {
    mini = tempc;
  } // set min temperature

  return tempc;
  sei();

}

// Timer2 interrupt routine -
// 1/(160000000/256/(256-6)) = 4ms interval

ISR(TIMER2_OVF_vect) {  
  TCNT2  = 6;
  tempref = ( 5.0 * analogRead(pin) * 100.0) / 1024.0;
  update_adc_key();
}

Your description of the problem is incomplete. I’m going to offer general advice and hope that by a stroke of luck stumble on a solution…

// Wrong data-type…
unsigned long lastTLswitch = 0;

void setup()
{

// From here…
// setup interrupt-driven keypad arrays
// reset button arrays
for(byte i=0; i<NUM_KEYS; i++){
button_count*=0;*
button_status*=0;
button_flag=0;
_ }
// …to here is not necessary. Remove it
[/quote]
> // Stop the timer while you make changes…
> TCCR2B = 0;
> TCCR2A = 0;
>
> // Setup timer2 – Prescaler/256*
> TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
> TCCR2B &= ~(1<<WGM22);
> TCCR2B = (1<<CS22)|(1<<CS21);
> // This line of code does nothing. Remove it or fix it.
> ASSR |=(0<<AS2);
> // You made various changes to TCCR2A earlier and then overwrote those changes here.
> // Use normal mode
> TCCR2A =0;
> // Does nothing. Remove it or fix it.
> //Timer2 Overflow Interrupt Enable
> TIMSK2 |= (0<<OCIE2A);
> // Does nothing. Interrupts are already enable at this point. Remove it.
> SREG|=1<<SREG_I;
> void temperature()
> {
> …
> // Wrong data-type and buggy.
> unsigned long lastUpdate = millis(); // Force update
> …
> // Update temp
> if( millis() - lastUpdate >= 1000) {
> int getTemp0()
> {
> int tempc = 0;
> // You CANNOT leave interrupts disabled for the entire function. This will cause a long list of problems including crippling millis.
> cli();
> …
> return tempc;
> // The following line of code will NEVER execute.
> sei();
> ISR(TIMER2_OVF_vect) {
> …
> // Floating-point math in an interrupt service routine may consume too much time.
> tempref = ( 5.0 * analogRead(pin) * 100.0) / 1024.0;_

Well, I'll take a look at it. Most of the code was reused from the forum, and I agree on quite a few of the comments. Hope to post solved code.

It works! I think the unsigned long did was the largest problem and of course the return statement before the sei().
I optimized the source.

heater_thermostat_lcd3310 (4.72 KB)