Restart loop () with push button

Good morning everyone.

I am doing a countdown timer on arduino UNO and a tft display ILI9341.

The thing is, I'm starting all this programming with arduino and I am totally stuck.

I've done a thousand searches on forums, google, youtube ....... but I can't get it to work as I wish.

The question; I need my countdown to end at "0" from a preset and unique time (59min59sec) and that when I press a button, the loop () is interrupted and restarted from the beginning.

My code:

#include <gfxfont.h>

#include "Fonts/FreeSans9pt7b.h"
#include <TFT_ILI9341.h>


#include <max6675.h>


#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <SPI.h>


#define PULSADOR 3
#define TFT_DC 9
#define TFT_CS 10


Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);


int S = 59; // cuenta segundos
int M = 59; // cuenta minutos
int H = 00; // cuenta horas






void setup() {
 
  pinMode(PULSADOR, INPUT_PULLUP);

  tft.fillScreen (ILI9341_BLACK);
 
  tft.setTextColor (ILI9341_WHITE, ILI9341_BLACK);  //punteros cronometro
  tft.setFont ();
  tft.setTextSize (4);
  tft.setCursor(93, 180);
  tft.print (":");
  tft.setCursor(203, 180);
  tft.print (":");


 

}


void loop(void) {


  //////cronometro//////

 if(digitalRead(PULSADOR)==HIGH);{

  S--;

 tft.setTextColor (ILI9341_WHITE, ILI9341_BLACK);
 tft.setTextSize (7);
 if(S<0)
 {
   M--;
   S=59;
 }
 if(M<0)
 {
   H--;
   M=59;
 }
 if(H<0) { H=00; M=00; S=00;
tft.print("FIN");}
 
 if(M>9)
 {
   tft.setCursor(120,170);
   tft.print(M);
 }
 else
 {
   tft.setCursor(120,170);
   tft.print("0");
   tft.setCursor(160,170);
   tft.print(M);
 }
 
 if(S>9)
 {
   tft.setCursor(230,170);
   tft.print(S);
 }
 else
 {
   tft.setCursor(230,170);
   tft.print("0");
   tft.setCursor(270,170);
   tft.print(S);
 }
 
 if(H>9)
 {
   tft.setCursor(10,170);
   tft.print (H);
 }
 else
 {
   tft.setCursor(10,170);
   tft.print("0");
   tft.setCursor(50,170);
   tft.print(H);
 }
}
return loop();
}

Any suggestion on how to do what I propose?
Thank you all

This is an interesting use of recursion:

return loop() ;

I guess it works if you don’t define too many local variables in the loop().

The semicolon after the if statement is wrong here:

if(digitalRead(PULSADOR)==HIGH) ;

6v6gt:
I guess it works if you don’t define too many local variables in the loop().

when you call a function there is always some context dumped onto the stack, so ultimately you'll end up in a stack overflow error.

  1. Read the tutorials section on waiting without using delay - Implement it to wait for 1 second.

  2. When millis() indicates one second has passed, use that to count down a single variable, timeLeft, from 3600 (60 * 60) to 0 on the display.

  3. Get rid of all your code checking and keeping track of individual H, M and S values and wrapping each. Hiding to nothing and so complex And unwieldy. Think what happens if you want days, weeks, months.
    Instead...

//every time one second has passed....
timeLeft = timeLeft - 1;
S = timeLeft % 60;
M = timeLeft / 60;
H = M / 60;
M = M % 60;
//Now display the values you calculated for H:M:S
  1. Detect when the button is pressed in loop()...
if (detected button was pressed)
{
  timeLeft = 3600;
}

Edit...
5. Learn how to use functions and write one to display a value as 2 digits on the LCD...

void display2digits(int n, int x, int y)
{
  //....
}

display2digits(H, .....);
display2digits(M, .....);
display2digits(S, ......);

J-M-L:
when you call a function there is always some context dumped onto the stack, so ultimately you'll end up in a stack overflow error.

Not in this specific case. That would be underestimating the compiler's ability to optimise such things out. I just did a simplified test with a recursive call to a loop() function, simulating what the OP did, to see how it behaves.
It converts the recursive call to a jump back to the beginning of the function.
Of course, it would have been different if the context of one invocation of the recursive function was dependent on an adjacent invocation etc.

Own main() to make assembler dump less cluttered:
Based on How to use main function to replace setup and loop? - Programming Questions - Arduino Forum post #9

#include <Arduino.h>
#undef main


void setup() {
 DDRB |= 0b10000000 ;  // pinMode(13, OUTPUT);
}

void loop() {
 PORTB |= 0b10000000 ;   // digitalWrite(13, HIGH);
 PORTB &= 0b01111111 ;   //digitalWrite(13, LOW);
 return ( loop() ) ;   // recursive loop call
}


int main()
{
 setup() ;

  while(1)
 {
   loop() ;
 }
 return 0;
}

assembler dump: from .elf file
see: How can one view the assembly code output from the Arduino IDE? - Programming Questions - Arduino Forum

C:\Users\6V6GT\Desktop>"C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-objdump.exe" -S C:\Users\6V6GT\Documents\sloeber-workspace\TEST3___main\Release\TEST3___main.elf > objdump.txt



C:\Users\6V6GT\Documents\sloeber-workspace\TEST3___main\Release\TEST3___main.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
   4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
   8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
   c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  50: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
  64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>

00000068 <__ctors_end>:
  68: 11 24       eor r1, r1
  6a: 1f be       out 0x3f, r1 ; 63
  6c: cf ef       ldi r28, 0xFF ; 255
  6e: d8 e0       ldi r29, 0x08 ; 8
  70: de bf       out 0x3e, r29 ; 62
  72: cd bf       out 0x3d, r28 ; 61
  74: 0e 94 40 00 call 0x80 ; 0x80 <main>
  78: 0c 94 44 00 jmp 0x88 ; 0x88 <_exit>

0000007c <__bad_interrupt>:
  7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>

00000080 <main>:
#include <Arduino.h>
#undef main


void setup() {
 DDRB |= 0b10000000 ;  // pinMode(13, OUTPUT);
  80: 27 9a       sbi 0x04, 7 ; 4
}

void loop() {
 PORTB |= 0b10000000 ;   // digitalWrite(13, HIGH);
  82: 2f 9a       sbi 0x05, 7 ; 5
    PORTB &= 0b01111111 ;   //digitalWrite(13, LOW);
  84: 2f 98       cbi 0x05, 7 ; 5
  86: fd cf       rjmp .-6       ; 0x82 <main+0x2>

00000088 <_exit>:
  88: f8 94       cli

0000008a <__stop_program>:
  8a: ff cf       rjmp .-2       ; 0x8a <__stop_program>

One tip: Learn about state-machines and how to code them.

loop() is just the function that detects and handles events, you should never try to call it directly yourself,
that's all automatic.

Think about the transitions you need to detect - time passing, button presses, etc, and detect them in loop()
with suitable code, pass the business logic out to another function to keep everything modular - no one function
should be large, and definitely don't put all your code inside loop() - that just creates an unreadable monolithic
slab of code. Organize by function, name functions well, make each function do just what its name suggests,
not 3 unrelated things.

6v6gt:
Not in this specific case.

interesting thanks.
I could see how this happens indeed as it's a void call, there is nothing to return and because code is ultra simple, no register to save.

you are almost there. you just need a timer in your sketch. read about the millis() function.

#include <gfxfont.h>

#include "Fonts/FreeSans9pt7b.h"
#include <TFT_ILI9341.h>


#include <max6675.h>


#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <SPI.h>


#define PULSADOR 3
#define TFT_DC 9
#define TFT_CS 10


Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);


int S = 59; // cuenta segundos
int M = 59; // cuenta minutos
int H = 00; // cuenta horas






void setup() {
 
  pinMode(PULSADOR, INPUT_PULLUP);

  tft.fillScreen (ILI9341_BLACK);
 
  tft.setTextColor (ILI9341_WHITE, ILI9341_BLACK);  //punteros cronometro
  tft.setFont ();
  tft.setTextSize (4);
  tft.setCursor(93, 180);
  tft.print (":");
  tft.setCursor(203, 180);
  tft.print (":");


 

}

unsigned long last;
void loop(void) {


  //////cronometro//////

 if(digitalRead(PULSADOR)==HIGH){
  M=59;S=59;H=0;
  }

// this next line creates a timer that happens once per second
if(millis()-last>1000){last=millis();
  S--;

 tft.setTextColor (ILI9341_WHITE, ILI9341_BLACK);
 tft.setTextSize (7);
 if(S<0)
 {
   M--;
   S=59;
 }
 if(M<0)
 {
   H--;
   M=59;
 }
 if(H<0) { H=00; M=00; S=00;
tft.print("FIN");}
 
 if(M>9)
 {
   tft.setCursor(120,170);
   tft.print(M);
 }
 else
 {
   tft.setCursor(120,170);
   tft.print("0");
   tft.setCursor(160,170);
   tft.print(M);
 }
 
 if(S>9)
 {
   tft.setCursor(230,170);
   tft.print(S);
 }
 else
 {
   tft.setCursor(230,170);
   tft.print("0");
   tft.setCursor(270,170);
   tft.print(S);
 }
 
 if(H>9)
 {
   tft.setCursor(10,170);
   tft.print (H);
 }
 else
 {
   tft.setCursor(10,170);
   tft.print("0");
   tft.setCursor(50,170);
   tft.print(H);
 }


}}

Hi.
Finally I have changed the code as suggested. It was hard for me because I don´t undestand the function millis() very well but it seems work.

This is my new code. Do you see it correct?

include"Wire.h"
#include <gfxfont.h>

#include "Fonts/FreeSansBold9pt7b.h"
#include "Fonts/FreeSans9pt7b.h"
#include <TFT_ILI9341.h>


#include <max6675.h>


#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <SPI.h>


#define start 3
#define LED_ROJO 2
#define TFT_DC 9
#define TFT_CS 10

#define ONE_SEC     1000
#define ONE_HOUR    (60*60)

unsigned long timeSec = 3600; //time seconds

//termocouple 1 - exhaust
int soPin = 5;
int cs1Pin = 6;
int sckPin = 7;
//termocouple 2 - cht
int cs2Pin = 4;


Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

MAX6675 thermocouple1(sckPin, cs1Pin, soPin);
MAX6675 thermocouple2(sckPin, cs2Pin, soPin);


void setup() {

  pinMode(LED_ROJO, OUTPUT);
  pinMode(start,INPUT_PULLUP);
  

  tft.begin();
  tft.setRotation(3);
  tft.fillScreen (ILI9341_WHITE);
  
  tft.fillScreen (ILI9341_BLACK);
  tft.setFont (&FreeSans9pt7b);
  tft.setTextColor (ILI9341_GREEN, ILI9341_BLACK);
  tft.setCursor (5,20);
  tft.print("ok");
  tft.setTextColor (ILI9341_WHITE, ILI9341_BLACK);
  tft.setCursor (40,50);
  tft.print ("escape (C)");
  tft.setCursor (200,50);
  tft.print ("cilindro (C)");
 
  tft.drawLine (160,30,160,110, ILI9341_WHITE);  //linea vertical pantalla
  tft.drawLine (0,110,320,110, ILI9341_WHITE);  //linea horizontal superior pantalla
  tft.drawLine (0,30,320,30, ILI9341_WHITE);
 
  tft.setCursor (100,130);
  tft.print ("tiempo STINT");

}
 
void loop() {


                                      //sensor EGT:
                                      
 if((thermocouple1.readCelsius())<30){
  digitalWrite(LED_ROJO, LOW);
  tft.setTextColor (ILI9341_GREEN, ILI9341_BLACK);
  tft.setTextSize (4);
  tft.setCursor (10,70);
  tft.print(thermocouple1.readCelsius());
  
 }
else{
  digitalWrite(LED_ROJO, HIGH);
  tft.setTextColor (ILI9341_RED, ILI9341_BLACK);
  tft.setTextSize (4);
  tft.setCursor (10,70);
  tft.print(thermocouple1.readCelsius());
  
}
                                         //sensor CHT:
                                          
  if((thermocouple2.readCelsius())<30){
  digitalWrite(LED_ROJO, LOW);
  tft.setTextColor (ILI9341_GREEN, ILI9341_BLACK);
  tft.setTextSize (4);
  tft.setCursor (170,70);
  tft.print(thermocouple2.readCelsius());

 }
else{
  digitalWrite(LED_ROJO, HIGH);
  tft.setTextColor (ILI9341_RED, ILI9341_BLACK);
  tft.setTextSize (4);
  tft.setCursor (170,70);
  tft.print(thermocouple2.readCelsius());
  
}

{
    chrono ();
}
}

void
chrono (void)
{
    static unsigned int msecLst = 0;
           unsigned int msec    = millis ();

    if (msec - msecLst > ONE_SEC)  {
        msecLst = msec;

        timeSec--;

        int min = timeSec / 60;
        int sec = timeSec % 60;

        char s [20];
        sprintf (s, "%2d:%02d", min, sec);
        tft.setTextSize (9);
        tft.setTextColor (ILI9341_WHITE, ILI9341_BLACK);
        tft.setCursor(30,170);
        tft.println (s);

        if (0 >= timeSec)\
           timeSec = ONE_HOUR;
    

}
}

now i'm trying to make the start button work as you suggested but it seems that it doesn't want to work

void setup() {
DDRB |= 0b10000000 ; // pinMode(13, OUTPUT);
}

void loop() {
PORTB |= 0b10000000 ; // digitalWrite(13, HIGH);
PORTB &= 0b01111111 ; //digitalWrite(13, LOW);
return ( loop() ) ; // recursive loop call
}

the pinMode is the 3, not the 13 sorry. Where I can find the assembler dump for this pin?

Thanks

6v6gt:
Own main() to make assembler dump less cluttered:

I did not know you could do that.
I learned something new today. My goal is to learn something new every day, so, now I can goof off for the next 23 hours.

setenta9:
void setup() {
DDRB |= 0b10000000 ; // pinMode(13, OUTPUT);
}

void loop() {
PORTB |= 0b10000000 ; // digitalWrite(13, HIGH);
PORTB &= 0b01111111 ; //digitalWrite(13, LOW);
return ( loop() ) ; // recursive loop call
}

the pinMode is the 3, not the 13 sorry. Where I can find the assembler dump for this pin?

Thanks

This code sample was intended to show only the consequences of directly calling loop() from within the loop(). You have already corrected that, so you can safely ignore this code. Since you did not mention that your program crashed, it raised an interesting question about how the compiler handled that situation.

SteveMann:
I did not know you could do that.
I learned something new today. My goal is to learn something new every day, so, now I can goof off for the next 23 hours.

I wasn't so sure about it either, but I've edited my post to add a link to give credit for idea (@robtillaart).