I need some help with a decrementing delay

I'm trying to write a routine that will ramp-up the decrement of a value the longer a button is pressed. It seems that a momentary press, less than my "debounce" delay still decrements about 13 instead of 1. Can anyone provide some guidance for me? I really don't understand why it isn't working correctly.

The purpose of this is to act like a clock adjustment. When you first push the button, the values change slowly. The longer you hold the button, the faster the numbers change.

//
//
//

int button1 = 11; //This is my input button
int coarse_value = 1000; //Eventually, I will have a fine_value adjustment also
int del = 100; //This is my delay while the button is pressed
int val; //The value used for a digital read
void setup()
{
pinMode(button1,INPUT);
digitalWrite(button1, LOW); //turn on the internal pulldown
Serial.begin(9600); //I'm using serial output for debugging purposes
}

void loop() {

val = digitalRead(button1); //Read the value of button1
delay(50); //Wait a short time, a debounce of sorts
Serial.print("coarse value= ");//print the values for debugging
Serial.println(coarse_value);
Serial.print("delay= ");
Serial.println(del);
if (val == LOW) //If I release the button, reset the delay
{
del = 100;
}
if (val == HIGH) //If I press the button, begin reducing the coarse_value value
{
coarse_value = (coarse_value-1);
if (del > 0)
{
del = (del-1); //As long as del >0, decriment. This is the speed ramp-up
delay(del); //for the coarse_value adjustment--the longer it's pressed,
//the faster it decriments
}
}

}

Here is the sketch I use for setting a clock using three buttons, perhaps it will be of some help. The swPressed function may be what you are looking for. You give it the pin number of the switch you are intersted in and it returns the number of milliseconds it has been pressed, or 0 if it has not been pressed for at least the debounce period. The rest of the code is an LCD clock and its an example of how to use swPressed. Pressing the Set button cycles through setting the hours, minutes and seconds. Pressing the inc and dec buttons increase and decrease the values, holding the inc/dec button down for at least one second speeds up the rate the values change

include <LiquidCrystal.h>
#include <DateTime.h>

#define DEBOUNCE_TIME  40 // switch must be pressed at least this number of ms
#define CLOCK_ROW  0  // row to display the time on the LCD
#define CLOCK_COL  0   // column for LCD display
#define NO_SWITCH  255  

#define btnSET 14  // pins for buttons
#define btnINC 15
#define btnDEC 16

//LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
LiquidCrystal lcd(7,6,5, 12,11,10,9); // my lcd wiring

void blinkingCursor(boolean enable){
// blink the cursor to show what units we are changing if enable is true
  if(enable)
    lcd.command(0xf);  // blink cursor
  else   
    lcd.command(0xc); // just enable the display
}

void setup(){
  Serial.begin(9600);
  pinMode(btnSET ,INPUT);
  pinMode(btnINC ,INPUT);
  pinMode(btnDEC ,INPUT);
  digitalWrite(btnSET, HIGH); // turn pull-ups on
  digitalWrite(btnINC, HIGH);
  digitalWrite(btnDEC, HIGH);
  lcd.begin();
  lcd.print("Press Set btn to set clock");
}

void loop(){
  if(DateTime.available()) { // update clocks if time has been synced
    digitalClockDisplay( );   // update digital clock
  }
  if(swPressed(btnSET)){
    if(DateTime.available()== false)
      DateTime.sync(1230768000); // if clock was never set,  start at Jan 1 2009
    DateTime.available();
    setTime();  
  }
  delay(100);
}

void digitalClockDisplay(){
  // digital clock display of current date and time
  lcd.setCursor( CLOCK_COL, CLOCK_ROW);  lcd.clear();   
  if(DateTime.Hour < 10)
    lcd.print(' ');
  lcd.print(DateTime.Hour,DEC);
  printDigits(DateTime.Minute);
  printDigits(DateTime.Second);
}

void printDigits(byte digits){
  // utility function for digital clock display: prints preceding colon and leading 0
  lcd.print(":");
  if(digits < 10)
    lcd.print('0');
  lcd.print(digits,DEC);
}

void setTime(){
  byte  second, minute, hour;

  digitalClockDisplay();
  if(swPressed(btnSET)) {
    lcd.setCursor( CLOCK_COL, CLOCK_ROW);
    blinkingCursor(true) ; // flash cursor
    while(swPressed(btnSET)) //wait for SET button to be released
      ;
  }    
  hour   = getValue(DateTime.Hour,0, 23, CLOCK_COL, CLOCK_ROW);  
  minute = getValue(DateTime.Minute,0, 59, CLOCK_COL+3, CLOCK_ROW);  
  second = getValue(DateTime.Second,0, 59, CLOCK_COL+6, CLOCK_ROW);  
  DateTime.sync(DateTime.makeTime(second, minute, hour, DateTime.Day, DateTime.Month, DateTime.Year));  //sets the time
  blinkingCursor(false) ; 
}


unsigned int swPressed(byte sw){
  // returns duration button pressed in ms, returns 0 if not pressed for DEBOUNCE period 
  // this logic only supports one button pressed at a time
  static byte prevswitch = NO_SWITCH;
  static unsigned long startTime;
  unsigned int dur;

  dur = 0;
  if(digitalRead(sw) == LOW){
    if(sw != prevswitch ){  
      startTime = millis();
      prevswitch = sw;     
      delay(DEBOUNCE_TIME); 
      if(digitalRead(sw) == LOW)
        dur = DEBOUNCE_TIME;
    }
    else if (sw == prevswitch ){
      Serial.println(millis() - startTime);
      if( millis() - startTime > 60000)
        dur = 60000; // this prevents overflowing return value
      else{
        dur = millis() - startTime;     
        if( dur < DEBOUNCE_TIME)
          dur = 0;  // return 0 if duration less than debounce time
      }
    }  
  }
  else if (sw == prevswitch ){  // was previous switch released?  
    prevswitch = NO_SWITCH;  // yes, so clear switch indicator
  } 
  return dur;
}

byte getValue(byte value, byte min, byte max,byte col, byte row){  
  //returns a value from min to max based on INC and DEC button presses 
  // displays the value at the given column and row

    //  lcd.setCursor(col,row); 
  //  lcd.print("--"); // print underline below selected digits
  //  lcd.command(0x4); // don't advance cursor
  while(1)   {
    int adj;
    lcd.setCursor(col,row); 
    if( value < 10)
      lcd.print('0');    
    lcd.print(value,DEC); //Print the current value            
    lcd.setCursor(col+1,row); 
    if(adj > 1000)
      delay(100);
    else
      delay(500);  

    adj = swPressed(btnINC);      
    if(adj){ 
      if(value >= max)
        value = min;
      else   
        value++;  
    }
    else{
      adj = swPressed(btnDEC);
      if(adj) 
        if(value == min)
          value = max;
        else   
          value--;        
    }
    if(swPressed(btnSET)){
      while(swPressed(btnSET))
        ; // wait for SET button to be released
      return value;   
    }
  }   
}

This is more like what I wanted to try. I guess one of my mistakes here also is that there is no internal pull down resistor, only a pull up. Therein my lie the problem of the inconsistent decrements.

Thanks for the code. I like this idea better anyway. I just didn’t know how to do it. I’ll study it.

Understanding switch states with pull-ups can be confusing. It may help to think of LOW as the state of the button being pressed down, HIGH is when the button goes back up.

Good luck with your project, I hope I will read more about it here or in the exhibition threads.

Have fun!

This is just one step in the process of a much larger project. My brother is an amateur photographer. He would like to do high-speed photos with his Nikon D70 to catch droplet splatters and such. I can do the hardware without much trouble. The challenge is in the software. I want to display on the LCD the millis and microsecond delay we are choosing. We have to build in about 100 milliseconds because of signaling to the camera. It will only remote trigger with an IR remote. If the delay in the camera is consistent, we should be able to overcome this.

I plan to use a laser or other light beam as a trigger. The droplet will break the beam and trigger the timer. Once the time expires, it triggers the camera to fire. If the IR remote triggering time and delay isn't consistent, we can always use the open shutter to trigger the flash instead.

Makes for a fun and challenging project. Is much less costly too. Commercially available products are more than $1,000 US. Ouch!

Sounds like a cool project.

BTW, have you seen this one: http://www.glacialwanderer.com/hobbyrobotics/?p=11

Yes, this is one of the sites that I've researched. Hopefully, it works as well as his did.

I tried changing to the internal pull ups. That was the problem. When I was trying to switch from low to high instead of high to low, I think the pin would float enough to keep the input active while the counter would decrement for 10+ counts. Using the internal pull ups, it would decrement only 1 count. I guess I've learned an important lesson.

Good to hear you have that licked. Part of the satisfaction of projects like this is overcoming these difficulties and knowing that you are building skill and knowledge that will make the next project easier. I look forward to seeing the write-up in the exhibition threads when you have it finished

Hey Mem, I've been swimming through your code for DateTime. I really like the way that it puts the values on the LCD and allows you to change them. That is really what I'd like to do for my project, but I'm having trouble understanding how it all works. It (the .pde file) calls the library and other files to define values and such, and I'm getting lost in the fray, but I'd really like to understand the onscreen setting of the clock function. Is there something of a tutorial available that might help me understand this?

There is only the information in the playground but I will be happy to add to that if it will help. A good place to start would be if you can list some of the questions you have – perhaps in the form of FAQ (for which I will try and provide answers)

I'd really like to understand the onscreen setting of the clock function

Do you mean the Processing sketch that is included in the download?

I think my confusion comes partly from the library. I know it's there to provide some functionality that you wouldn't be able to do as cleanly in a single program. I've been going over each of the functions in the program and I believe it is beginning to make sense. It just isn't coming as fast as I want and I think that is the frustration.

My goal is to be able to use the blinking cursor selector like you did, but I want to change milli-seconds and micro-seconds. I'm going to play around with it a bit and see what I can come up with. I'll get back with you if I have trouble.

Where can I find information about libraries and keywords and their usage, etc? The keywords file is something I'm not familiar with. It may be basic C programming, but I'm not schooled in C, other than since I started with Arduino a couple of months ago. I have used other very basic languages, so I can catch on fairly quickly, but it is taking some time.

Perhaps I confused you with the example sketch I posted above. It is the swPressed function that I wanted you to look at, this returns the duration in milliseconds that a button is pressed. The example sketch showing its use happens to use the DateTime library but that is probably not what you need for your app. The sketch does show how you can use the duration of a button press to speed up a counter, but I don't think DateTime is needed for what you want.

Perhaps you can write a sketch that just uses swPressed to display the duration of the button press, then add the functionality to increment/decrement a counter based on how long the button is pressed, and then build your application around that

Sorry for my delay in getting back to you. There are, in fact, some very good aspects of the datetime sketch you posted. The decrimenting delay was part of a larger project. I wanted to be able to do the decrementing delay so you wouldn't have to wait for a large number to go by.

I like the ability to choose a digit and increment or decrement it. I'm having trouble understanding exactly how that part works. I want to do a delay for high-speed photography. I want to choose the milli-seconds and the micro-seconds to delay on one screen and then go to the next step, very much like datetime. Trouble is, I'm not sure how all of those things are working together. The concept is exactly what I'm looking for, though.

Chad

DateTime works by using the Arduino millis function to calculate the number of elapsed seconds. It accumulates the number of seconds and converts these into times and dates. I don't think the methods used would work for the microsecond timing you are looking for.
You do something with the micros function, but although it sound similier to millis, it works completely differently and the logic used in DateTime wont work. You could also use a timer to do your count but these are not easy to work with. There is a library called msTimer2 in the playground but this has a resolution of 1 millisecond and this may not be enough for you.

Perhaps if you say more about the requirements for your application it will be easer to suggest a solution. For example, what are the minimum and maximum time intervals you need to set? How many microseconds error is acceptable? What needs to happen (if anything) while the timer is running?

Since it is so difficult to find a high-speed video of a water droplet splatter, I won't really know how critical the timing is until I can test this.
The idea is that we want to set a time both milli-seconds and micro-seconds for the arduino to delay. The delay is triggered by a light beam or a microphone, whatever the application is for the shot. At the end of the delay, it sends the infrared command to the camera to fire. Milli-seconds may be close enough, but I don't know for sure. Do know anyone who has experience with high-speed shooting? It would be great to see some frame-by-frame timing. That would help the project greatly.

Can you do some experiments before designing the user interface? Why not set the time duration from the serial port. You can enter values using the Serial monitor and read them in Arduino using code similar to the following:

void setup()
{
  Serial.begin(9600); 
}

void loop()
{
  static int dur = 0;

  if ( Serial.available()) {
    char ch = Serial.read();

    switch(ch) {
    case '0'...'9':
      dur = dur * 10 + ch - '0';
      break;
    case 'u':
      trigger_uS(dur);
      dur = 0;
      break;

    case 'm':
      trigger_mS(dur);
      dur = 0;
      break;
    }
  }
}

void trigger_uS(int duration)
{
  // start trigger here
  delayMicroseconds( duration);
  // end trigger here   
}

void trigger_mS(int duration)
{
  // start trigger here
  delay( duration);
  // end trigger here    
}

Enter a numeric value between 1 and 999 followed by a 'u' for microseconds or 'm' for milliseconds

This would give me enough to tell what kind of timing I'm looking at. Thanks for the code. I'll give it a try. I'll let you know how it goes. I really appreciate your time on this.