timing problems

Hello Forum, I’m new to posting here. I’m having trouble with timing a LED (0-60mins) at the push of a button. From what I can understand, from reading thru the forum and googling, I should be able to keep time for that long without an RTC. I have tried to work thru examples with a Tiny RTC module to address my LED timing and I’m not able to figure it out.

My project background: I’m trying to make a product similar to this:

[Power Link 2](http://Power Link 2- Assistive Technology-Power Link 2 Control Unit)

I don’t plan on making money off of it. It would be used by my niece.

The product makes it possible to use an assistive tech switch to switch an AC circuit on or off. The product has four modes:
Direct (push the button turns on, let go turns off), Latch (push button turns on, stays on until button is pressed again), Timed(minutes) and Timed(seconds), both these modes work the same push the button AC light/fan etc. stays on for a certain amount of time.

I’ve pieced together code that covers all of the modes. Right now I’m using a LED in place of a relay. I’ll be using this product to do the actual relay control:

Controllable Four Outlet Power Relay Module

I’m playing it safer since this will be for a three year old handicapped child.

My issue is that when I am in the Timed(minute) mode the LED doesn’t turn off when set to the longest time(60min) after an hour. I’ve tried mapping different max values without success. What follows is finally the code.

working_relay_timer.ino (7.42 KB)

#include <Adafruit_NeoPixel.h>

#define BUTTON_PIN   4     // Digital IO pin connected to the button.

#define PIXEL_PIN    3     // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 12

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

bool oldState = HIGH;
int showType = 0;

//colors
uint32_t red = strip.Color(255, 0, 0);
uint32_t yellow = strip.Color(255, 150, 0);
uint32_t green = strip.Color(0, 255, 0);
uint32_t blue = strip.Color(0, 0, 255);
uint32_t black = strip.Color(0, 0, 0);

// constants won't change. They're used here to set pin numbers:
const int switch_pin = 2;      //the number of the pushbutton pin
const int ledPin =  13;       //the number of the LED pin

// variables will change:
int switch_State = 0;          //variable for reading the pushbutton status
int inner_State = 0;

int state = HIGH;       //the current state of the output pin
int reading;            //the current reading from the input pin
int previous = LOW;     //the previous reading from the input pin

// the follow variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long time = 0;          //the last time the output pin was toggled
long debounce = 200;   //the debounce time, increase if the output flickers

//TIMED VARIABLES//
int ledState = LOW;              //ledState used to set the LED
unsigned long previousMillis = 0;        //will store last time LED was updated
const long interval = 1000;           //interval at which to blink (milliseconds)

//TIMED POT VARIABLES//
unsigned long switch_pinPushedMillis;  //when switch_pin was released
unsigned long ledPinTurnedOnAt;  //when ledPin was turned on
unsigned long turnOnDelay = 0;  //wait to turn on ledPin
unsigned long turnOffDelay = 1;  //turn off ledPin after this time
bool ledPinReady = false;  //flag for when switch_pin is let go
bool ledPinState = false;  //for ledPin is on or not.
int potPin = A0;     //select the input pin for the potentiometer
int val = 0;        //variable to store the value coming from the sensor


//SETUP//

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(switch_pin, INPUT_PULLUP);
  digitalWrite(ledPin, LOW);
  strip.begin();
  strip.setBrightness(10);  //50% Brightness 10/255
  strip.show();  //Initialize all pixels to 'off'
  Serial.begin(9600);
}

 // Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

void startShow(int i) {
  switch (i) {
    case 0: colorWipe(black, 50);     //Black/off
      break;
    case 1: colorWipe(red, 50);   //Red (DIRECT Mode)
      direct();
      break;
    case 2: colorWipe(yellow, 50);   //Yellow (TIMED(Sec) Mode)
      timed_sec();
      break;
    case 3: colorWipe(green, 50);   //Green (TIMED(Min) Mode)
      timed_min();
      break;
    case 4: colorWipe(blue, 50);  // Blue (Latch Mode)
      latch();
      break;
  }
}

//MODE FUNCTIONS//

void direct() {
  Serial.println("Direct Mode");
  do {
    inner_State = digitalRead(BUTTON_PIN);
    Serial.println(inner_State);
    switch_State = digitalRead(switch_pin);
     //read the state of the pushbutton value:
    // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
    if (switch_State == LOW) {
//       turn LED on:
      digitalWrite(LED_BUILTIN, HIGH);    //turn the LED on (HIGH is the voltage level)
    } else {
//       turn LED off:
      digitalWrite(LED_BUILTIN, LOW);     //turn the LED off by making the voltage LOW
    }
  } while (inner_State == 1);
}

void timed_sec() {
  Serial.println("Timed Seconds Mode");
  do {
    inner_State = digitalRead(BUTTON_PIN);
    Serial.println(inner_State);
// get the time at the start of this loop()
 unsigned long currentMillis = millis(); 
 val = analogRead(potPin);     //read the value from the sensor
 turnOffDelay = map(val, 0, 1023, 0, 60000); //minutes
  //check the switch_pin
 if (digitalRead(switch_pin) == LOW) {
   //update the time when switch_pin was pushed
  switch_pinPushedMillis = currentMillis;
  ledPinReady = true;
 }
  
//  make sure this code isn't checked until after switch_pin has been let go
 if (ledPinReady) {
//   this is typical millis code here:
   if ((unsigned long)(currentMillis - switch_pinPushedMillis) >= turnOnDelay) {
      //okay, enough time has passed since the switch_pin was let go.
     digitalWrite(ledPin, HIGH);
      //setup our next "state"
     ledPinState = true;
      //save when the ledPin turned on
     ledPinTurnedOnAt = currentMillis;
      //wait for next switch_pin press
     ledPinReady = false;
   }
 }
  
  //see if we are watching for the time to turn off ledPin
 if (ledPinState) {
    //okay, ledPin on, check for now long
   if ((unsigned long)(currentMillis - ledPinTurnedOnAt) >= turnOffDelay) {
     ledPinState = false;
     digitalWrite(ledPin, LOW);
   }
 }  
  } while (inner_State == 1);
}

void timed_min() {
  Serial.println("Timed Minutes Mode");
  do {
    inner_State = digitalRead(BUTTON_PIN);
    Serial.println(inner_State);
 //get the time at the start of this loop()
 unsigned long currentMillis = millis(); 
 val = analogRead(potPin);     //read the value from the sensor
 turnOffDelay = map(val, 0, 1023, 0, 3600000); //hours
  //check the switch_pin
 if (digitalRead(switch_pin) == LOW) {
   //update the time when switch_pin was pushed
  switch_pinPushedMillis = currentMillis;
  ledPinReady = true;
 }
  
//  make sure this code isn't checked until after switch_pin has been let go
 if (ledPinReady) {
//   this is typical millis code here:
   if ((unsigned long)(currentMillis - switch_pinPushedMillis) >= turnOnDelay) {
      //okay, enough time has passed since the switch_pin was let go.
     digitalWrite(ledPin, HIGH);
      //setup our next "state"
     ledPinState = true;
      //save when the ledPin turned on
     ledPinTurnedOnAt = currentMillis;
      //wait for next switch_pin press
     ledPinReady = false;
   }
 }
  
  //see if we are watching for the time to turn off ledPin
 if (ledPinState) {
    //okay, ledPin on, check for now long
   if ((unsigned long)(currentMillis - ledPinTurnedOnAt) >= turnOffDelay) {
     ledPinState = false;
     digitalWrite(ledPin, LOW);
   }
 } 
  } while (inner_State == 1);
}

void latch() {
  Serial.write("Latch Mode");
  do {
    inner_State = digitalRead(BUTTON_PIN);
    Serial.println(inner_State);
    reading = digitalRead(switch_pin);

//   if the input just went from LOW and HIGH and we've waited long enough
//   to ignore any noise on the circuit, toggle the output pin and remember
//   the time
  if (reading == LOW && previous == HIGH && millis() - time > debounce) {
    if (state == LOW)
      state = HIGH;
    else
      state = LOW;

    time = millis();    
  }

  digitalWrite(ledPin, state);

  previous = reading;
  } while (inner_State == 1);
}


void loop() {
   //Get current button state.
  bool newState = digitalRead(BUTTON_PIN);

   //Check if state changed from high to low (button press).
  if (newState == LOW && oldState == HIGH) {
     //Short delay to debounce button.
//    delay(20);
     //Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
      showType++;
      if (showType > 4)
        showType = 0;
      startShow(showType);
    }
  }

   //Set the last button state to the old state.
  oldState = newState;
}
   //update the time when switch_pin was pushed

Are you really pushing the pin?

You only call startShow() when the mode changes. When the mode changes to 3, causing timed_min() to be called, timed_min() sets stuff up and returns. It never gets called again.

You need to keep calling timed_min() until it has done its job.

Can a function run like a loop? If it could would this work?

    case 3: colorWipe(green, 50);   //Green (TIMED(Min) Mode)
      do{
        timed_min();
      } while {startShow == 3);

If it could would this work?

No, because nothing in the body of the loop statement changes the value in startShow, so the do/while loop will never end.

loop() loops. Make use of that.

The only value that comes out of my loop is showType. and if I use that in do/while loop like before, then I get stuck in the do/while loop. I'm trying to understand.

I also, tried putting the similar code from both my timed_min() and timed_sec() functions in the main loop(). That didn't work. I was following the logic that I wanted keep time when switch_pin was pushed.

So, I'm not sure what part of the timed functions need to move to the loop and what needs to remain in the function. It seems to either get stuck in a non-responsive main loop or a non-responsive do/while loop.

The only value that comes out of my loop is showType

Values do not come out of loops.

Please pay attention to the last line of my last post. If you want an function called by startShow() to run to completion, just use delay, and don't make it necessary to call the function over and over.

If you do want to be able to change the show type, to stop the AC even if the hour isn't up, for instance, then let loop() do ALL of the looping.

If you want each function, as called by loop(), to run to completion and then stop, have a new value for showType that means do nothing, and set showType to that value in each function, when it has done doing whatever it needs to do.