How do I put a start/stop/reset code in my timer?

Hi,

I'm really new to coding, so I really have no idea how to code or understand the terms. I have an assignment that needs to have a button that starts/stops and resets like a stopwatch timer using millis. I've heard of debouncing but cant seem to wrap my head around the coding aspect of it. If someone can help me with it, even a link that shows this. Would be 100% awesome..

This is my code that I have so far

#include <U8glib.h>

U8GLIB_SSD1351_128X128_332 u8g(13, 11, 8, 9, 7); // Arduino UNO: SW SPI Com: SCK = 13, MOSI = 11, CS = 8, DC = 9, RESET = 7 (http://electronics.ilsoft.co.uk/ArduinoShield.aspx)

int Heart = A0;
int value = 0;
int Motor = 12;
int motorRead = 6;
int Thresh = 350;
int ReadRate = 25;
int UnderCount = 0;
int OverCount = 0;
String OutValue = ""; 

char messageBuffer[50] = {0}; 

void setup() {
    // flip screen, if required
  // u8g.setRot180();
  
  // set SPI backup if required
  //u8g.setHardwareBackup(u8g_backup_avr_spi);

  // assign default color value
  if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
    u8g.setColorIndex(255);     // white
  }
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
    u8g.setColorIndex(3);         // max intensity
  }
  else if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
  else if ( u8g.getMode() == U8G_MODE_HICOLOR ) {
    u8g.setHiColorByRGB(255,255,255);
  }
  
  // put your setup code here, to run once:
  Serial.begin(19200);
  pinMode(Motor, OUTPUT);
  //delay(5000);
}

void loop() {
    // picture loop
  u8g.firstPage();  
  do {
    draw();
  } while( u8g.nextPage() );

  // put your main code here, to run repeatedly:
  value = analogRead(Heart);
  Serial.println(value);
  if (value > 550){
    OverCount = OverCount + 1;
    UnderCount = UnderCount + 1;
  }
  else{
     UnderCount = UnderCount + 1;     
  }
   
    float TestF =  (OverCount/(UnderCount*0.1)*100);
    String OutValue = String (TestF/2);
    //Serial.println(OutValue + " BPM");
    Serial.println(value);  

  if (value > Thresh ){
      digitalWrite(Motor, HIGH);
      digitalWrite(motorRead, HIGH);
      delay(50);
      digitalWrite(Motor, LOW);
      digitalWrite(motorRead, LOW);
  }
  else{
      delay(50);
  }
  
  if (UnderCount > 50){
      UnderCount = 0;
      OverCount = 0;
  }
  delay(ReadRate);

  stopWatch(); 
}

void draw(void) {
  // graphic commands to redraw the complete screen should be placed here  
  u8g.setFont(u8g_font_unifont);
  //u8g.setFont(u8g_font_osb21);
  //int value = 0;
  //char buf[9];
  //sprintf (buf, "%d", value);  
  //u8g.drawStr( 0, 40, buf);



  String value = "test";
  value = analogRead(Heart);
  u8g.setPrintPos( 0, 40 );
  u8g.print(messageBuffer);
  u8g.setPrintPos( 0, 80);
  u8g.print(value);
  u8g.print(" BPM"); 
}

void stopWatch(void)  {

  memset(messageBuffer, '\0', sizeof(messageBuffer)); 
 
  static unsigned long current_time;
  unsigned long remainder, minutes, seconds, milliseconds; 

  current_time = millis(); 

  milliseconds = current_time % 1000; 
  remainder = current_time / 1000;
  seconds = remainder % 60;
  remainder /= 60;
  minutes = remainder % 60;

  addToMessageBuffer(minutes, 2, true);
  addToMessageBuffer(seconds, 2, true); 
  addToMessageBuffer(milliseconds, 3, false); 

  Serial.println(messageBuffer); 
}

void addToMessageBuffer(unsigned long digit, int len, bool appendColon) {

  char str[10] = {0};

  //Convert digit to string
  sprintf(str, "%lu", digit); 

  //Pad with as many zeros as desired
  while (strlen(str) < len) {
      char temp[10] = {0};
      strcat(temp, "0"); 
      strcat(temp, str); 
      strcpy(str, temp); 
  }

  //Add to messageBuffer
  strcat(messageBuffer, str); 

  //Append colon if desired
  if (appendColon == true) {
    strcat(messageBuffer, ":"); 
  }
}

what research have you done about buttons and bouncing?

I've done research, so I kind of understand how it works and the idea of it. So I get the code of using an LED and a button then coding it like

const int buttonPin = 2;    
const int ledPin =  13;     
int ledState = LOW;
boolean buttonState = LOW; 

int pressed=0;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void loop() {
  
  if(debounceButton(buttonState) == HIGH && buttonState == LOW)
  {
    pressed++;
    buttonState = HIGH;
  }
  else if(debounceButton(buttonState) == LOW && buttonState == HIGH)
  {
       buttonState = LOW;
  }
   if(pressed == 2)
  {
    digitalWrite(ledPin,HIGH);
  }
}

boolean debounceButton(boolean state)
{
  boolean stateNow = digitalRead(buttonPin);
  if(state!=stateNow)
  {
    delay(10);
    stateNow = digitalRead(buttonPin);
  }
  return stateNow;
  
}

But I cant get my head around coding, for the timer and connecting that to the LCD screen

the challenge with that debouching technic is that there is a delay... so might not be exactly what you want given you want to measure time... so delay() are not great for this your code will be stuck there (even if in practice here it won't matter much)

what you need to do:

detect the first press of the button: this is the call to action and you can start the stopwatch

then if you see release and press events happening quickly (during 15ms or so) thereafter, this is bouncing, you can ignore that and not stop the stopwatch - you really just need to wait for the full release of the button (i.e. stop bouncing and return to unpressed stage for a while)

then the next press (before possibly bouncing you will ignore again) is when to stop the stopwatch

If not debouncing how would you go about it? Because I'm just looking for a way that I can stop my timer, although I'm using millis that wouldnt effect the use of debouncing right? In my head I want to use a tactile button that when I press once it starts, when i press again later on it stops and the when I press once again it resets.

Do you possible have an example I may look at to get more of an understanding?

My project the timer is for like a long distant running something you wear on the wrist, so for full press it shouldnt matter. How would I change the connection of the LED to the timer on the code?

Thanks for your help as well! much appreciated!

You need to debounce, but you do not need to debounce with a call to delay(); that's the "poor man's debounce"

See for example this tutorial which is not using delay()

Maybe this simple stopwatch sketch will help get you started, connect a pushbutton between pin 4 and GND.

//simple stopwatch
uint32_t start, dbStart;
float eTime;
const byte btn = 4, led = 13, dbTime = 15;
bool pin4State = true,
     btnState = true,
     prevBtnState = true,
     timing = false;
void setup()
{
  Serial.begin(9600);
  pinMode(btn,INPUT_PULLUP);
  pinMode(led,OUTPUT);
  Serial.println(F("  Simple Stopwatch\n\n\
  Press button to start timing\n"));
}
void loop()
{
  // debounce button ++++++++++++++++
  if(digitalRead(btn) != pin4State)  // get state of pin 4
  {
    dbStart = millis(); // reset db timer
    pin4State = !pin4State;      // now they are equal, won't enter
  }                     // here again unless pin state changes
  if (millis() - dbStart > dbTime) // db timer has elapsed
    btnState = pin4State;           // button state is valid 
//+++++++++++++++++++++++++++++++++++
  if(btnState != prevBtnState && !btnState) // btnState changed
  {
    // if button pressed and NOT timing
    if(!timing)
    {
      start = millis(); // reset stopwatch
      eTime = 0;        // reset elapsed time
      timing = true;    // prevents entry while timing  
      Serial.println(F("  Press button to stop\n"));
    }
    else
    {
      // if button pressed while timing,
      // stop, calculate, display
      eTime = (millis() - start) / 1000.0;
      timing = false;
      Serial.print(F("  Elapsed seconds = "));
      Serial.print(eTime); Serial.print("\t");
      Serial.println("\n");
      Serial.println(F("  Press button to start timing\n"));
    }
  }
  prevBtnState = btnState;
  digitalWrite(led,timing);
}

Good luck :slight_smile:

Thank you!