UV exposure timer problems

So I am on my first project and I am trying to build a countdown timer that uses a rotary encoder with a push button, as well as two other buttons for start/pause, and stop.

I have tried to fumble with my code and pieced it together, but I am lost after reading several resources and staring at this for the last few days.

What it does currently is it will display Timer ready with 0 0 below. If I hit the start button it goes straight to RING RING mode. It does not detect the long press.

I am probably 10 feet underwater on this and bite too much off for a starter project.

I have done a blink LED program and a button control to test everything and get my feet wet. Then dove in with lead on.

Here is my garbage code: (I am doing two posts to cover it)

#include <CL_RotaryEncoder.h>

#include <LiquidCrystal.h>

#include <Time.h>
#include <TimeLib.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int BuzzerPin = 8;
const int StartButtonPin = 14;
const int StartButtonLongPin = 14; //Do I need this?
const int StopButtonPin = 15;
const int RelayPin = 10;
const int OutputA = 6; //rotary encoder A
const int OutputB = 7; //rotary encoder B
const int RotaryButtonPin = 9;

int setupHours = 0;     // How many hours will count down when started
int setupMinutes = 0;   // How many minutes will count down when started
int setupSeconds = 0;   // How many seconds will count down when started
time_t setupTime = 0;

int currentHours = 0;
int currentMinutes = 0;
int currentSeconds = 0;
time_t currentTime = 0;

time_t startTime = 0;
time_t elapsedTime = 0;

int counter = 0; 
int aState;
int aLastState; 
int StopButtonState = LOW;
long StartButtonLongPressCounter = 0;
int StartButtonState = LOW;
int RotaryButtonState = LOW;
int relayPin = LOW;
int StopButtonPrevState = LOW;
int StartButtonPrevState = LOW;
int RotaryButtonPrevState= LOW;
bool StopButtonPressed = false;
bool StartButtonLongPressed = false;
bool StartButtonPressed = false;
bool RotaryButtonPressed = false;

const int MODE_IDLE = 0;
const int MODE_SETUP = 1;
const int MODE_RUNNING = 2;
const int MODE_RINGING = 3;

int currentMode = MODE_IDLE;    // 0=idle 1=setup 2=running 3=ringing
                                // Power up --> idle
                                // Stop --> idle
                                //  Start/pause --> start or pause counter
                                //  rotaryB / rotaryA --> NOP
                                // Start (long press) --> enter setup
                                //   rotaryButton --> data select
                                //   rotaryB --> increase current data value 
                                //   rotaryA --> decrease current data value
                                //   Stop --> exit setup (idle)

int dataSelection = 0;  // Currently selected data for edit (setup mode, changes with Start/Stop)
                        // 0=hours (00-99) 1=minutes (00-59) 2=seconds (00-59)


void setup() {
   // put your setup code here, to run once:
lcd.begin(16, 2);
  pinMode(StopButtonPin, INPUT);
  pinMode(StartButtonPin, INPUT);
  pinMode(RotaryButtonPin, INPUT);
  pinMode (OutputA, INPUT);
  pinMode (OutputB, INPUT);
  pinMode(BuzzerPin, OUTPUT);
  Serial.begin(9600);
   // Reads the initial state of the outputA
   aLastState = digitalRead(OutputA);
}

void loop() {
  // put your main code here, to run repeatedly:
StartButtonPressed = false;
StopButtonPressed = false;
RotaryButtonPressed = false;
StartButtonLongPressed = false;
//  upButtonPressed = false; //this needs changed to work with a rotary encoder - rotaryB
//  downButtonPressed = false; //this needs changed to work with a rotary encoder - rotaryA

  /*
   * Stop button management
   */
  StopButtonPressed = false;
  StopButtonState = digitalRead(StopButtonPin);
  if(StopButtonState != StopButtonPrevState)
  {
    StopButtonPressed = StopButtonState == HIGH;
    StopButtonPrevState = StopButtonState;
  }

  /*
   * Start button management
   */
StartButtonPressed = false;
  StartButtonLongPressed = false;
  StartButtonState = digitalRead(StartButtonPin);
  if(StartButtonState != StartButtonPrevState)
  {
    StartButtonPressed = StartButtonState == HIGH;
    StartButtonPrevState = StartButtonState;
  }
  else  // Long press management...
  {
    if(StartButtonState == HIGH)
    {
      StartButtonLongPressCounter++;
      if(StartButtonLongPressCounter == 100)
      {
        StartButtonPressed = false;
        StartButtonLongPressed = true;
        StartButtonLongPressCounter = 0;
      }
    }
    else
    {
      StartButtonLongPressCounter = 0;
      StartButtonPressed = false;
      StartButtonLongPressed = false;
    }
  }
   // rotary encoder
  aState = digitalRead(OutputA); // Reads the "current" state of the outputA
   // If the previous and the current state of the outputA are different, that means a Pulse has occured
   if (aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(OutputB) != aState) { 
       counter ++;
     } else {
       counter --;
     }
     Serial.print("Position: ");
     Serial.println(counter);
    
   aLastState = aState; // Updates the previous state of the outputA with the current state
  }

  /*
   * Mode management
   */
  switch(currentMode)
  {
    case MODE_IDLE:
      if(StopButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      if(StartButtonLongPressed)
      {
        currentMode = MODE_SETUP;
      }
      if(StartButtonPressed)
      {
        currentMode = currentMode == MODE_IDLE ? MODE_RUNNING : MODE_IDLE;
        if(currentMode == MODE_RUNNING)
        {
          // STARTING TIMER!
          startTime = now();
        }
      }
      break;

Here is the last half:

case MODE_SETUP:    
      if(StopButtonPressed)
      {
        // Exit setup mode
        setupTime = setupSeconds + (60 * setupMinutes);
        currentMinutes = setupMinutes;
        currentSeconds = setupSeconds;
        dataSelection = 0;
        currentMode = MODE_IDLE;
      }
      if(aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(OutputB) != aState) { 
       dataSelection ++;
     } else {
       dataSelection --;
     }
        // Select next data to adjust 
        dataSelection++;
        if(dataSelection == 2)
        {
          dataSelection = 0;
        }
      }
      if(aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(OutputB) != aState) { 
       dataSelection ++;
     } else {
       dataSelection --;
     }
        switch(dataSelection)
        {
          case 0: // minutes
            setupMinutes--;
            if(setupMinutes == -1)
            {
              setupMinutes = 59;
            }
            break;
          case 1: // seconds
            setupSeconds--;
            if(setupSeconds == -1)
            {
              setupSeconds = 59;
            }
            break;
        }
      }
      if(aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(OutputB) != aState) { 
       dataSelection ++;
     } else {
       dataSelection --; 
     }
        switch(dataSelection)
        {
          case 0: // minutes
            setupMinutes++;
            if(setupMinutes == 60)
            {
              setupMinutes = 0;
            }
            break;
          case 1: // seconds
            setupSeconds++;
            if(setupSeconds == 60)
            {
              setupSeconds = 0;
            }
            break; 
        }
      }
      break;
    
    case MODE_RUNNING:
      if(StartButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      if(StopButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      break;

    case MODE_RINGING:
      if(StartButtonPressed || StopButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      break;
  }

  /*
   * Time management
   */
  switch(currentMode)
  {
    case MODE_IDLE:
      break;
    case MODE_SETUP:
      // NOP
      break;
    case MODE_RUNNING:
      currentTime = setupTime - (now() - startTime);
      digitalWrite(relayPin, HIGH); 
      if(currentTime <= 0)
      {
        currentMode = MODE_RINGING;
      }
      break;
    case MODE_RINGING:
      digitalWrite(relayPin, LOW);
      analogWrite(BuzzerPin, 20);
      delay(20);
      analogWrite(BuzzerPin, 0);
      delay(40);
      break;
  }

  /*
   * LCD management
   */
  //lcd.clear();
  lcd.setCursor(0, 0);
  switch(currentMode)
  {
    case MODE_IDLE:
      lcd.print("Timer ready     ");
      lcd.setCursor(0, 1);
      lcd.print(currentMinutes);
      lcd.print(" ");
      lcd.print(currentSeconds);
      lcd.print("    ");
      break;
    case MODE_SETUP:
      lcd.print("Setup mode: ");
      switch(dataSelection)
      {
        case 0:
          lcd.print("MINS");
          break;
        case 1:
          lcd.print("SECS");
          break;
      }
      lcd.setCursor(0, 1);
      lcd.print(setupMinutes);
      lcd.print(" ");
      lcd.print(setupSeconds);
      lcd.print("    ");
      break;
    case MODE_RUNNING:
      lcd.print("Counting down...");
      lcd.setCursor(0, 1);
      if(minute(currentTime) < 10) lcd.print("0");
      lcd.print(minute(currentTime));
      lcd.print(":");
      if(second(currentTime) < 10) lcd.print("0");
      lcd.print(second(currentTime));
      break;
    case MODE_RINGING:
      lcd.print("   RING-RING!   ");
      lcd.setCursor(0, 1);
      lcd.print("                ");
      break;
  }
  delay(10);
}

void Reset()
{
  currentMode = MODE_IDLE;
  currentMinutes = setupMinutes;
  currentSeconds = setupSeconds;
}
StartButtonPressed = false;
StopButtonPressed = false;
RotaryButtonPressed = false;
StartButtonLongPressed = false;
//  upButtonPressed = false; //this needs changed to work with a rotary encoder - rotaryB
//  downButtonPressed = false; //this needs changed to work with a rotary encoder - rotaryA

  /*
   * Stop button management
   */
  StopButtonPressed = false;

Maybe you need to set StopButtonPressed to false a few more times.

Fancy comments add NOTHING to your code

// Stop button management

is more than sufficient.

     StartButtonLongPressCounter++;

What, EXACTLY, are you counting? Whatever it is, you do NOT need to count ANYTHING.

When the switch BECOMES pressed, record the time. When the switch BECOMES released, record the time. The interval between the presses tells you whether it was a short press or a lllllllllloooooooooooonnnnnnnnnnnggggggggggg press.

Thanks for the response. Most of this code is based off of this Kitchen Timer project.

I am starting from scratch with this code and using 5 buttons (start/pause, stop, rotary button, up, and down) to see if I can get this working. Then I will come back and change to a rotary encoder, and finally worry about switching the LED panel relay on.

The counter was going to detect if I held the start/pause button down for longer than X amount of time. I will also clean up some of the comments to take away sources of error.

Here is my reworked code with the 5 buttons. I am still getting it to to say Timer ready, and the only button to cause a change it the start/pause button. When pressed though it goes straight to RING RING.

/**************************************************
 * Arduino Kitchen Timer v1.0 - 2016/01/27
 * By Angelo Fiorillo (Rome, IT)
 * This work is distributed under the GNU General 
 * Public License version 3 or later (GPL3+)
 * Please include this credit note if you want to 
 * re-use any part of this sketch. Respect my work 
 * as I'll do with yours.
 * Feel free to contact me: afiorillo@gmail.com
 * ************************************************/
#include <LiquidCrystal.h>
#include <Time.h>
#include <TimeLib.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int buzzerPin = 8;
const int stopButtonPin = 15;
const int startpauseButtonPin = 14;
//const int rotaryAPin = 7; eventually an encoder
//const int rotaryBPin = 6; eventually an encoder
const int rotaryButtonPin = 9;
const int relayPin = 10;
const int upButtonPin = 7; //fix for rotary encoder A
const int downButtonPin = 6; //fix for rotary encoder B

int setupMinutes = 0;   // How many minutes will count down when started
int setupSeconds = 0;   // How many seconds will count down when started
time_t setupTime = 0;

int currentMinutes = 0;
int currentSeconds = 0;
time_t currentTime = 0;

time_t startTime = 0;
time_t elapsedTime = 0;

int stopButtonState = LOW;
long startpauseButtonLongPressCounter = 0;
int startpauseButtonState = LOW;
int rotaryButtonState = LOW;
int upButtonState = LOW; //fix for rotary encoder A
int downButtonState = LOW; //fix for rotary encoder B
int stopButtonPrevState = LOW;
int startpauseButtonPrevState = LOW;
int rotaryButtonPrevState = LOW;
int upButtonPrevState = LOW; //fix for rotary encoder A
int downButtonPrevState = LOW; //fix for rotary encoder B
bool stopButtonPressed = false;
bool startpauseButtonLongPressed = false;
bool startpauseButtonPressed = false;
bool rotaryButtonPressed = false;
bool upButtonPressed = false; //fix for rotary encoder A
bool downButtonPressed = false; //fix for rotary encoder B

const int MODE_IDLE = 0;
const int MODE_SETUP = 1;
const int MODE_RUNNING = 2;
const int MODE_RINGING = 3;

int currentMode = MODE_IDLE;    // 0=idle 1=setup 2=running 3=ringing
                                // Power up --> idle
                                // Stop --> idle
                                //  Start/Pause --> start or stop counter
                                //  Rotary encoder input --> NOP
                                // Start/Pause (long press) --> enter setup
                                //   Rotary Button --> data select
                                //   Rotary Encoder CW --> increase current data value
                                //   Rotary Encoder CCW --> decrease current data value
                                //   Stop --> exit setup (idle)

int dataSelection = 0;  // Currently selected data for edit (setup mode, changes with Start/Pause)
                        // 0=minutes (00-59) 1=seconds (00-59)

void setup() {
  // put your setup code here, to run once, I need to straighten out encoder setup
  lcd.begin(16, 2);
  pinMode(stopButtonPin, INPUT);
  pinMode(startpauseButtonPin, INPUT);
  pinMode(rotaryAPin, INPUT);
  pinMode(rotaryBPin, INPUT);
  pinMode(rotaryButtonPin, INPUT);
  pinMode(buzzerPin, OUTPUT);
  pinMode(relayPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  startpauseButtonPressed = false;
  stopButtonPressed = false;
  upButtonPressed = false; //fix for rotary encoder A
  downButtonPressed = false; //fix for rotary encoder B
  rotaryButtonPressed = false;

  // Stop button management
   
  stopButtonPressed = false;
  stopButtonState = digitalRead(stopButtonPin);
  if(stopButtonState != stopButtonPrevState)
  {
    stopButtonPressed = stopButtonState == HIGH;
    stopButtonPrevState = stopButtonState;
  }

  // Start/Pause button management 
  
  startpauseButtonPressed = false;
  startpauseButtonLongPressed = false;
  startpauseButtonState = digitalRead(startpauseButtonPin);
  if(startpauseButtonState != startpauseButtonPrevState)
  {
    startpauseButtonPressed = startpauseButtonState == HIGH;
    startpauseButtonPrevState = startpauseButtonState;
  }
  else  // Long press management...
  {
    if(startpauseButtonState == HIGH)
    {
      startpauseButtonLongPressCounter++;
      if(startpauseButtonLongPressCounter == 100)
      {
        startpauseButtonPressed = false;
        startpauseButtonLongPressed = true;
        startpauseButtonLongPressCounter = 0;
      }
    }
    else
    {
      startpauseButtonLongPressCounter = 0;
      startpauseButtonPressed = false;
      startpauseButtonLongPressed = false;
    }
  }

  //Rotary button management
  
  rotaryButtonPressed = false;
  rotaryButtonState = digitalRead(rotaryButtonPin);
  if(rotaryButtonState != rotaryButtonPrevState)
  {
    rotaryButtonPressed = rotaryButtonState == HIGH;
    rotaryButtonPrevState = rotaryButtonState;
  }
  
  // Down button management
  
  downButtonPressed = false;
  downButtonState = digitalRead(downButtonPin);
  if(downButtonState != downButtonPrevState)
  {
    downButtonPressed = downButtonState == HIGH;
    downButtonPrevState = downButtonState;
  }


  // Up button management
  
  upButtonPressed = false;
  upButtonState = digitalRead(upButtonPin);
  if(upButtonState != upButtonPrevState)
  {
    upButtonPressed = upButtonState == HIGH;
    upButtonPrevState = upButtonState;
  }

Here is the last half. (I found two typos that included the rotary encoder and those have been changed.

// Mode management
   
  switch(currentMode)
  {
    case MODE_IDLE:
      if(stopButtonPressed)
      {
        Reset();
      }
      if(startpauseButtonLongPressed)
      {
        currentMode = MODE_SETUP;
      }
      if(startpauseButtonPressed)
      {
        currentMode = currentMode == MODE_IDLE ? MODE_RUNNING : MODE_IDLE;
        if(currentMode == MODE_RUNNING)
        {
          // STARTING TIMER!
          startTime = now();
        }
      }
      break;

    case MODE_SETUP:
      if(stopButtonPressed)
      {
        // Exit setup mode
        setupTime = setupSeconds + (60 * setupMinutes);
        currentMinutes = setupMinutes;
        currentSeconds = setupSeconds;
        dataSelection = 0;
        currentMode = MODE_IDLE;
      }
      if(rotaryButtonPressed)
      {
        // Select next data to adjust
        dataSelection++;
        if(dataSelection == 2)
        {
          dataSelection = 0;
        }
      }
      if(downButtonPressed)
      {
        switch(dataSelection)
        {
          case 0:// minutes
            setupMinutes--;
            if(setupMinutes == -1)
            {
              setupMinutes = 59;
            }
            break;
          case 1: // seconds
            setupSeconds--;
            if(setupSeconds == -1)
            {
              setupSeconds = 59;
            }
            break;
        }
      } 
      if(upButtonPressed)
      {
        switch(dataSelection)
        {
          case 0: // minutes
            setupMinutes++;
            if(setupMinutes == 60)
            {
              setupMinutes = 0;
            }
            break;
          case 1: // seconds
            setupSeconds++;
            if(setupSeconds == 60)
            {
              setupSeconds = 0;
            }
            break;
        }
      }
      break;
    
    case MODE_RUNNING:
      if(startpauseButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      if(stopButtonPressed)
      {
        Reset();
        currentMode = MODE_IDLE;
      }
      break;

    case MODE_RINGING:
      if(stopButtonPressed || startpauseButtonPressed || downButtonPressed || upButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      break;
  }

  /*
   * Time management
   */
  switch(currentMode)
  {
    case MODE_IDLE:
    case MODE_SETUP:
      // NOP
      break;
    case MODE_RUNNING:
      currentTime = setupTime - (now() - startTime);
      if(currentTime <= 0)
      {
        currentMode = MODE_RINGING;
      }
      break;
    case MODE_RINGING:
      analogWrite(buzzerPin, 20);
      delay(20);
      analogWrite(buzzerPin, 0);
      delay(40);
      break;
  }

  /*
   * LCD management
   */
  //lcd.clear();
  lcd.setCursor(0, 0);
  switch(currentMode)
  {
    case MODE_IDLE:
      lcd.print("Timer ready     ");
      lcd.setCursor(0, 1);
      lcd.print(currentMinutes);
      lcd.print(" ");
      lcd.print(currentSeconds);
      lcd.print("    ");
      break;
    case MODE_SETUP:
      lcd.print("Setup mode: ");
      switch(dataSelection)
      {
        case 0:
          lcd.print("MINS");
          break;
        case 1:
          lcd.print("SECS");
          break;
      }
      lcd.setCursor(0, 1);
      lcd.print(setupMinutes);
      lcd.print(" ");
      lcd.print(setupSeconds);
      lcd.print("    ");
      break;
    case MODE_RUNNING:
      lcd.print("Counting down...");
      lcd.setCursor(0, 1);
      if(minute(currentTime) < 10) lcd.print("0");
      lcd.print(minute(currentTime));
      lcd.print(":");
      if(second(currentTime) < 10) lcd.print("0");
      lcd.print(second(currentTime));
      break;
    case MODE_RINGING:
      lcd.print("   RING-RING!   ");
      lcd.setCursor(0, 1);
      lcd.print("                ");
      break;
  }
  delay(10);
}

void Reset()
{
  currentMode = MODE_IDLE;
  currentMinutes = setupMinutes;
  currentSeconds = setupSeconds;
}

OK so I said screw it and started by doing that exact sketch for the Kitchen Timer. After some debugging some issues (wrong pin and missing ground wire). I have got a working timer. I am going to now redo one thing at a time. I didn't need hours for this sketch, so I originally deleted it. I have decided to keep it and try to KISS.

I will first try to add my encoder and take out the up and down buttons.

Ok so I have managed to tweak my programming a little and have a functional timer that will turn on my panel when it is running, and off when it is not. My final goal is to switch out the up and down buttons for a rotary encoder. So what is the right way to insert the following code into my current up and down controls? The pulses in the code would be changed to setupMinutes or setupSeconds depending on the case I am in. I know this will involve moving some pins around because I will need to use pin 2&3 for the encoder.

int pulses, A_SIG=0, B_SIG=1;

void setup(){
  attachInterrupt(0, A_RISE, RISING);
  attachInterrupt(1, B_RISE, RISING);
  Serial.begin(115200);
}//setup


void loop(){
    
}

void A_RISE(){
 detachInterrupt(0);
 A_SIG=1;
 
 if(B_SIG==0)
 pulses++;//moving forward
 if(B_SIG==1)
 pulses--;//moving reverse
 Serial.println(pulses);
 attachInterrupt(0, A_FALL, FALLING);
}

void A_FALL(){
  detachInterrupt(0);
 A_SIG=0;
 
 if(B_SIG==1)
 pulses++;//moving forward
 if(B_SIG==0)
 pulses--;//moving reverse
 Serial.println(pulses);
 attachInterrupt(0, A_RISE, RISING);  
}

void B_RISE(){
 detachInterrupt(1);
 B_SIG=1;
 
 if(A_SIG==1)
 pulses++;//moving forward
 if(A_SIG==0)
 pulses--;//moving reverse
 Serial.println(pulses);
 attachInterrupt(1, B_FALL, FALLING);
}

void B_FALL(){
 detachInterrupt(1);
 B_SIG=0;
 
 if(A_SIG==0)
 pulses++;//moving forward
 if(A_SIG==1)
 pulses--;//moving reverse
 Serial.println(pulses);
 attachInterrupt(1, B_RISE, RISING);
}

but replacing the following functions

if(downButtonPressed)
      {
        switch(dataSelection)
        {
          case 0: // hours
            setupHours--;
            if(setupHours == -1)
            {
              setupHours = 99;
            }
            break;
          case 1: // minutes
            setupMinutes--;
            if(setupMinutes == -1)
            {
              setupMinutes = 59;
            }
            break;
          case 2: // seconds
            setupSeconds--;
            if(setupSeconds == -1)
            {
              setupSeconds = 59;
            }
            break;
        }
      }
      if(upButtonPressed)
      {
        switch(dataSelection)
        {
          case 0: // hours
            setupHours++;
            if(setupHours == 100)
            {
              setupHours = 0;
            }
            break;
          case 1: // minutes
            setupMinutes++;
            if(setupMinutes == 60)
            {
              setupMinutes = 0;
            }
            break;
          case 2: // seconds
            setupSeconds++;
            if(setupSeconds == 60)
            {
              setupSeconds = 0;
            }
            break;

There is NO reason to detach the interrupt handler, and reattach it, in the ISR. When the ISR is running, interrupts are disabled, so the handler will not try to call the ISR again.

You won't have a down switch any more, so you'll need to have some other way of determining that there is a need to invoke the switch statement. What is that going to be?