HALO-Photographs 'The remote shutter release timer for NIKON D300 project' Project Index Page

Code for the electronic remote shutter release timer for a NIKON D300

This code has been tested on a FREEDUINO and a ATMEGA168 Board (both 100% ARDUINO compatible)

You may 'copy-paste' the marked section below and/or download the code

The layout of the rather simple wiring will be published here soon.

Creative Commons License
Code for the electronic remote shutter release timer for a NIKON D300 by Hans Loepfe is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.5 Switzerland License.
Based on a work at newyorkpanorama.com.
Permissions beyond the scope of this license may be available at https://www.halo-photographs.com/info/obtain-license.html.

The code for ARDUINO and compatibles

Arduino long exposure bracketing

/* 
This program allows controlled bracketing specially suited for long exposures > 1/6 second.
Measure the maximum und minimum exposre time, select the corresponding values on RSRT which calcultes and displays what it's goint to.
Basically just fire away.
 -----
Arduino long exposure bracketing
Parts of the LCD menu and interrupt timer are taken from the nokia demo application at nuelectronics.com
 -----
 2009 Joergen Geerds, V0.32 released as a creative commons open source license, please give/leave credit
 download and more info at
 http://newyorkpanorama.com/2009/01/21/long-exposure-night-hdr-photography-with-arduino/
 ----- 
 modified by Hans Loepfe (2009/05/12) and re-released under a creative commons BY-NC-SA license,
 the code is available at: https://www.halo-photographs.com/remote-shutter-release-timer/Arduino/Nikon-RSRT-052.7zip
 Version V0.52-HALO
 -----
 Some modification and other notes:
 - First I have rewritten the firing sequence to accomodate to a NIKON D300 and introduced an additional digital signaling pin.
 - Also the 'Bracket Dance' has been adapted using a different approach.
 The Bracketwidth became the Bracketstep. From a base exposure (center) the sequence simply follows up and down the register
 of 'exptime[]' by increments defined in the 'EV settings' for as many shots (uneven numbers) as calculated.
 - The menus show a bit more information and the shooting sequence shows more values. Those I have used as and aid for getting
 the code do what I want it to do.
 - I had to adjust the timing values for the NIKON D300 that requires exactly 600ms in addition to the acutal exposure time.
 This was neccessary for me because when shooting bracketed shots, I always set the Mirror-up function to ON. This creates a 600ms delay.
 - I call this program 'RSRT' (aka remote-shutter-rellease-timer) - what else - run it off a small 9V Battery and carry it allover the place 
 to shot panoramic HDRIs.
 - Unfortunately sometimes, when flipping through the settings pages, the code crashes, but quickly restarts.
 I tried to figure out the cause, but did not succeed. I assume it has something to do with the addl. variables I introduced (see line 76-86).
 Or possibly with the memory available for the display. I don't know.
 When someone finds the answers, please be so kind and post the results back to me, thanks.

Turning 'Noise-Reduction' OFF
If one uses a NIKON D300 with 'Noise-Reduction for long exposures' turned on, then the D300 displays 'Job Nr' for as long as the previous exposure (sarting at 20 secs).
Therefore the bracketing sequence and the fire() must wait (brktwait) for the exposure period of the previous shot (b) before it can continue with the next shot.
The code lines below (on lines 387)take this into account.
If one prefers to turn OFF 'Noise-Reduction for long exposures', then the lines marked '// *ON' below become obsolete
If one prefers to turn ON 'Noise-Reduction for long exposures', then the lines marked '// **OFF below become obsolete
Expect this part of the code to be changed as a case selection in the NENU.

 - Now use the code, improve and enjoy it.
 ...and don't forget to use it according to the Creative Commons license BY-NC-SA. 
*/

#include "Nokia_lcd.h"

//keypad debounce parameter
#define DEBOUNCE_MAX 15
#define DEBOUNCE_ON 10
#define DEBOUNCE_OFF 3

#define NUM_KEYS 5
// joystick number
#define U_K 3    // up
#define L_K 0    // left
//#define C_K 1  // center, not used
#define D_K 2    // down
#define R_K 4    // right

// general wait timing between focus, mirror-up and shot. Used in the 'fire()' sequence only
int INITfocus = 2000;	// wait 2 secondes for the camera to get focus and into the 'ready' state
int INITfire = 400;	// this actually releases the shutter (400)
int SHUTTERPIN = 4;	// the pin the shutter release is connected to?
int FOCUS = 6;         // Camera 'ready' (FOCUS)
int RELEASE = 7;       // pushbutton pin
int EVMIN = 0;        // min 1/10 second (11)
int EVMAX = 44;        // max 120 minutes
int t1 = 25;          // temporary delay after joystick activity
int t2 = 25;          // temporary delay before write to display


// exposure value table in 0.5 EV steps in milliseconds (600ms added to compensate the camera shutter lag)
unsigned long exptime[] = {602, 603, 604, 606, 608, 611, 617, 622, 633, 650, 667, 700, 728, 767, 850, 933, 1100, 1267, 1600, 2100, 2600, 3600, 4600, 6600, 8600, 10600, 15600, 20600, 30600, 40600, 60600, 90600, 120600, 180600, 240600, 360600, 480600, 600600, 900600, 1200600, 1800600, 2400600, 3600600, 4800600, 7200600, 14401200};
char* expstr[] = {"1/500", "1/350s", "1/250s", "1/180s", "1/125s", "1/90s", "1/60s", "1/45s", "1/30s", "1/20s", "1/15s", "1/10s", "1/8s", "1/6s", "1/4s", "1/3s", "1/2s", "2/3s", "1sec", "1.5sec", "2sec", "3sec", "4sec", "6sec", "8sec", "10sec", "15sec", "20sec", "30sec", "40sec", "1min", "1.5min", "2min", "3min", "4min", "6min", "8min", "10min", "15min", "20min", "30min", "40min", "60min", "80min", "120min", "240min"};

unsigned long brktwait[] = {1000, 2500, 5000, 10000, 15000, 20000};
char* brktstr[] = {"1s", "2.5s", "5s", "10s", "15s", "20s"};

// default values for exposure bracketing
byte b = 18;		// base exposure, 1sec from exptime[]  (18)
byte w = 2;		// bracket steps EV/2 , default 1EV
byte n = 5;		// # of bracketed shots, 5
byte c = 1;		// wait time before next shot, optional, default='2.5s', (needed if 'Noise-Reduction for long exposures' is set to 'ON' - see also at the end of the 'fire()' sequence.
byte s = 0;		// # of shots taken during firing sequence
byte ev_min = 11;       // shortest exposure time, measured & entered by user
byte ev_max = 30;       // longest exposure time, measured & entered by user
byte ev_base;    // base exposure time, calculated
byte x;      // used to calculate the # of shots above and below the base shot
byte i;     // generic counter

	
// string variables
char tmpstr[8];      // generic string
// char tmpstr0[6];       // designated string for exposuretime b


// adc preset value, represent top value,incl. noise & margin,that the adc reads, when a key is pressed
// set noise & margin = 30 (0.15V@5V)
int adc_key_val[NUM_KEYS] = {30, 128, 260, 374, 515};

byte button_count[NUM_KEYS];    // debounce counters
byte button_status[NUM_KEYS];    // button status - pressed/released
byte button_flag[NUM_KEYS];    // button on flags for user program 

// Display Settings, write position top-left
#define MN_X 5
#define MN_Y 5

// seemingly not needed
void main_menu(void);
void menu_expmax(void);	    //menu item inserted - enter max exposure
void menu_expmin(void);	    //menu item inserted - enter min exposure
void menu_bracketstep(void);
void menu_brktwait(void);	//menu item inserted - time delay between shots, if desired


Nokia_lcd lcd=Nokia_lcd();
void InitPort(){
DDRB=0x2F;
}

void setup(void){
// setup interrupt-driven keypad arrays  
// reset button arrays
	for(i=0; i<NUM_KEYS; i++){
	button_count[i]=0;
	button_status[i]=0;
	button_flag[i]=0;
	}
  
// Setup timer2 -- Prescaler/256
	TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
	TCCR2B &= ~(1<<WGM22);
	TCCR2B = (1<<CS22)|(1<<CS21);      
	ASSR |=(0<<AS2);
	// Use normal mode  
	TCCR2A =0;    
	//Timer2 Overflow Interrupt Enable  
	TIMSK2 |= (0<<OCIE2A);
	TCNT2=0x6;  // counting starts from 6;  
	TIMSK2 = (1<<TOIE2);    
	SREG|=1<<SREG_I;
	InitPort();
	LCD_BACKLIGHT(1);
	lcd.cLCD_Init();
	pinMode(RELEASE, INPUT); 
}

void loop(void){
	button_flag[i]=0;          // reset button flag
	main_menu();               // make main menu
	while(true){               // repeat forever
	for(i=0; i<NUM_KEYS; i++){		// wait for anything to happen
	if(digitalRead(RELEASE) == LOW){	// red button on pin RELEASE is pressed (goes LOW)
		bracket();         // start taking shots
	}
	if(button_flag[i] !=0){
		button_flag[i]=0;     // reset button flag
	switch(i){
	case L_K:
        delay(t1);
        menu_brktwait();
//        menu_bracketstep();
	break;
	case R_K:
        delay(t1);
        menu_expmax();
	break;
	}
	}
	}
	}
}

void main_menu(void){
        ev_base=round((ev_max+ev_min)/2);
        b=ev_base;
        x = (round((ev_base-ev_min)/2));  //calculating # of shots above and below the base shot
        n = 1 + (2 * x);
        delay(t2);
	lcd.cLCD_Box(0,0, 131,131, FILL, WHITE);         // clear screen
	lcd.cLCD_String("Ready...", MN_X, MN_Y, GREEN, BLACK);
	lcd.cLCD_String("Press Button", MN_X, MN_Y+10, GREEN, BLACK);
	lcd.cLCD_String("Exposures:", MN_X, MN_Y+20, BLACK, WHITE);
        lcd.cLCD_String("Base:", MN_X, MN_Y+30, BLACK, WHITE);
	lcd.cLCD_String(itoa(ev_base, tmpstr, 10), MN_X+40, MN_Y+30, RED, WHITE);
	lcd.cLCD_String(expstr[b], MN_X+65, MN_Y+30, RED, WHITE);
	lcd.cLCD_String("Max:", MN_X, MN_Y+40, BLACK, WHITE);
	lcd.cLCD_String(itoa(ev_max, tmpstr, 10), MN_X+40, MN_Y+40, RED, WHITE);
	lcd.cLCD_String(expstr[ev_max], MN_X+65, MN_Y+40, RED, WHITE);
	lcd.cLCD_String("Min:", MN_X, MN_Y+50, BLACK, WHITE);
	lcd.cLCD_String(itoa(ev_min, tmpstr, 10), MN_X+40, MN_Y+50, RED, WHITE);
	lcd.cLCD_String(expstr[ev_min], MN_X+65, MN_Y+50, RED, WHITE);
	lcd.cLCD_String("up/down:", MN_X, MN_Y+60, BLACK, WHITE);
	lcd.cLCD_String(itoa(x, tmpstr, 10), MN_X+65, MN_Y+60, RED, WHITE);
	lcd.cLCD_String("Total:", MN_X, MN_Y+70, BLACK, WHITE);
	lcd.cLCD_String(itoa(n, tmpstr, 10), MN_X+65, MN_Y+70, RED, WHITE);
	lcd.cLCD_String("Bracketstep(s):", MN_X, MN_Y+80, BLACK, WHITE);
	lcd.cLCD_String("  /2 EV", MN_X+25, MN_Y+90, BLACK, WHITE);
	lcd.cLCD_String(itoa(w, tmpstr, 10), MN_X+25, MN_Y+90, RED, WHITE);
	lcd.cLCD_String("Bracket wait:", MN_X, MN_Y+100, BLACK, WHITE);
	lcd.cLCD_String(brktstr[c], MN_X+25, MN_Y+110, RED, WHITE);
}

void menu_expmax(void){
        b=ev_max;
  	button_flag[i]=0;	// reset button flag
        delay(t2);
	lcd.cLCD_Box(0,0, 131,131, FILL, WHITE);	// clear screen
	lcd.cLCD_String("Select", MN_X, MN_Y, BLUE, LIGHTBLUE);
	lcd.cLCD_String("Longest Exposure:", MN_X, MN_Y+10, BLUE, LIGHTBLUE);
	lcd.cLCD_String("      ", MN_X+20, MN_Y+25, RED, WHITE);
	lcd.cLCD_String(expstr[b], MN_X+20, MN_Y+25, RED, WHITE);
	lcd.cLCD_String("Millisecs:", MN_X, MN_Y+60, BLACK, WHITE);
	lcd.cLCD_String("          ", MN_X, MN_Y+70, RED, WHITE);
// 	lcd.cLCD_String(ultoa(exptime[b], tmpstr0, 10), MN_X, MN_Y+70, RED, WHITE);
	lcd.cLCD_String(ultoa(exptime[b], tmpstr, 10), MN_X, MN_Y+70, RED, WHITE);
        lcd.cLCD_String("Register:", MN_X, MN_Y+80, BLACK, WHITE);
        lcd.cLCD_String("          ", MN_X+80, MN_Y+80, RED, WHITE);
        lcd.cLCD_String(itoa(b, tmpstr, 10), MN_X+80, MN_Y+80, RED, WHITE);    //print current register# in array
	while(true){
	for(i=0; i<NUM_KEYS; i++){	// wait for anything to happen
	if(button_flag[i] !=0){
		button_flag[i]=0;		// reset button flag
		switch(i){
  	case U_K:
        delay(t1);
	if (b<= EVMAX-1) b=b+1;
		lcd.cLCD_String("       ", MN_X+20, MN_Y+25, RED, WHITE);
		lcd.cLCD_String(expstr[b], MN_X+20, MN_Y+25, RED, WHITE);
	        lcd.cLCD_String("          ", MN_X, MN_Y+70, RED, WHITE);
// 		lcd.cLCD_String(ultoa(exptime[b], tmpstr0, 10), MN_X, MN_Y+70, RED, WHITE);
		lcd.cLCD_String(ultoa(exptime[b], tmpstr, 10), MN_X, MN_Y+70, RED, WHITE);

                ev_max=b;
                lcd.cLCD_String("          ", MN_X+80, MN_Y+80, RED, WHITE);                
                lcd.cLCD_String(itoa(b, tmpstr, 10), MN_X+80, MN_Y+80, RED, WHITE);    //print current register# in array
	break;
	case D_K:
        delay(t1);
	if (b>=EVMIN+1) b=b-1;
		lcd.cLCD_String("       ", MN_X+20, MN_Y+25, RED, WHITE);
		lcd.cLCD_String(expstr[b], MN_X+20, MN_Y+25, RED, WHITE);
	        lcd.cLCD_String("          ", MN_X, MN_Y+70, RED, WHITE);
//		lcd.cLCD_String(ultoa(exptime[b], tmpstr0, 10), MN_X, MN_Y+70, RED, WHITE);
		lcd.cLCD_String(ultoa(exptime[b], tmpstr, 10), MN_X, MN_Y+70, RED, WHITE);
                ev_max=b;
                lcd.cLCD_String("          ", MN_X+80, MN_Y+80, RED, WHITE);
                lcd.cLCD_String(itoa(b, tmpstr, 10), MN_X+80, MN_Y+80, RED, WHITE);    //print current register# in array
	break;
	case L_K:
        delay(t1);
	loop();
	case R_K:
        delay(t1);
	menu_expmin();
	}
	}
	}
	}
}

void menu_expmin(void){
        b=ev_min;
  	button_flag[i]=0;	// reset button flag
        delay(t2);
	lcd.cLCD_Box(0,0, 131,131, FILL, WHITE);	// clear screen
	lcd.cLCD_String("Select", MN_X, MN_Y, BLUE, LIGHTBLUE);
	lcd.cLCD_String("Shortest Exposure:", MN_X, MN_Y+10, BLUE, LIGHTBLUE);
	lcd.cLCD_String("      ", MN_X+20, MN_Y+25, RED, WHITE);
	lcd.cLCD_String(expstr[b], MN_X+20, MN_Y+25, RED, WHITE);
	lcd.cLCD_String("Millisecs:", MN_X, MN_Y+60, BLACK, WHITE);
	lcd.cLCD_String("          ", MN_X, MN_Y+70, RED, WHITE);
// 	lcd.cLCD_String(ultoa(exptime[b], tmpstr0, 10), MN_X, MN_Y+70, RED, WHITE);
	lcd.cLCD_String(ultoa(exptime[b], tmpstr, 10), MN_X, MN_Y+70, RED, WHITE);
        lcd.cLCD_String("Register:", MN_X, MN_Y+80, BLACK, WHITE);
        lcd.cLCD_String("          ", MN_X+80, MN_Y+80, RED, WHITE);
        lcd.cLCD_String(itoa(b, tmpstr, 10), MN_X+80, MN_Y+80, RED, WHITE);    //print current register# in array
	while(true){
	for(i=0; i<NUM_KEYS; i++){	// wait for anything to happen
	if(button_flag[i] !=0){
		button_flag[i]=0;		// reset button flag
		switch(i){
  	case U_K:
        delay(t1);
	if (b<= EVMAX-1) b=b+1;
		lcd.cLCD_String("       ", MN_X+20, MN_Y+25, RED, WHITE);
		lcd.cLCD_String(expstr[b], MN_X+20, MN_Y+25, RED, WHITE);
	        lcd.cLCD_String("          ", MN_X, MN_Y+70, RED, WHITE);
//		lcd.cLCD_String(ultoa(exptime[b], tmpstr0, 10), MN_X, MN_Y+70, RED, WHITE);
		lcd.cLCD_String(ultoa(exptime[b], tmpstr, 10), MN_X, MN_Y+70, RED, WHITE);
                ev_min=b;
                lcd.cLCD_String("          ", MN_X+80, MN_Y+80, RED, WHITE);                
                lcd.cLCD_String(itoa(b, tmpstr, 10), MN_X+80, MN_Y+80, RED, WHITE);    //print current register# in array
	break;
	case D_K:
        delay(t1);
	if (b>=EVMIN+1) b=b-1;
		lcd.cLCD_String("       ", MN_X+20, MN_Y+25, RED, WHITE);
		lcd.cLCD_String(expstr[b], MN_X+20, MN_Y+25, RED, WHITE);
	        lcd.cLCD_String("          ", MN_X, MN_Y+70, RED, WHITE);
// 		lcd.cLCD_String(ultoa(exptime[b], tmpstr0, 10), MN_X, MN_Y+70, RED, WHITE);
		lcd.cLCD_String(ultoa(exptime[b], tmpstr, 10), MN_X, MN_Y+70, RED, WHITE);
                ev_min=b;
                lcd.cLCD_String("          ", MN_X+80, MN_Y+80, RED, WHITE);                
                lcd.cLCD_String(itoa(b, tmpstr, 10), MN_X+80, MN_Y+80, RED, WHITE);    //print current register# in array
	break;
	case L_K:
        delay(t1);
	menu_expmax();
	case R_K:
        delay(t1);
	menu_bracketstep();
	}
	}
	}
	}
}

void menu_bracketstep(void){
        delay(t2);
	button_flag[i]=0;  // reset button flag
	lcd.cLCD_Box(0,0, 131,131, FILL, WHITE);         // clear screen
	lcd.cLCD_String("Select", MN_X, MN_Y, BLUE, LIGHTBLUE);
	lcd.cLCD_String("Bracketstep(s):", MN_X, MN_Y+10, BLUE, LIGHTBLUE);
	lcd.cLCD_String("  /2 EV", MN_X+10, MN_Y+25, BLACK, WHITE);
	lcd.cLCD_String(itoa(w, tmpstr, 10), MN_X+10, MN_Y+25, RED, WHITE);
	while(true){
	for(i=0; i<NUM_KEYS; i++){		// wait for anything to happen
	if(button_flag[i] !=0){
		button_flag[i]=0;	// reset button flag
		switch(i){
	case U_K:
        delay(t1);
	if (w<12) w++;		//+-12 EV should be enough
		lcd.cLCD_String("  ", MN_X+10, MN_Y+25, RED, WHITE);
		lcd.cLCD_String(itoa(w, tmpstr, 10), MN_X+10, MN_Y+25, RED, WHITE);
	break;
	case D_K:
        delay(t1);
	if (w>1) w--;
		lcd.cLCD_String("  ", MN_X+10, MN_Y+25, RED, WHITE);
		lcd.cLCD_String(itoa(w, tmpstr, 10), MN_X+10, MN_Y+25, RED, WHITE);
	break;
	case L_K:
        delay(t1);
        menu_expmin();
	case R_K:
        delay(t1);
	menu_brktwait();
//	loop();
	}
	}
	}
	}
}

void menu_brktwait(void){
        delay(t2);
	button_flag[i]=0;  // reset button flag
	lcd.cLCD_Box(0,0, 131,131, FILL, WHITE);         // clear screen
	lcd.cLCD_String("Select", MN_X, MN_Y, BLUE, LIGHTBLUE);
	lcd.cLCD_String("Bracket wait:", MN_X, MN_Y+10, BLUE, LIGHTBLUE);
	lcd.cLCD_String("    ", MN_X+20, MN_Y+25, RED, WHITE);
	lcd.cLCD_String(brktstr[c], MN_X+20, MN_Y+25, RED, WHITE);

	while(true){
	for(i=0; i<NUM_KEYS; i++){		// wait for anything to happen
	if(button_flag[i] !=0){
		button_flag[i]=0;  // reset button flag
	switch(i){
	case U_K:
        delay(t1);
	if (c<5) c++;	// 6 options (1s, 2.5s, 5s, 10s, 15s, 20s)
		lcd.cLCD_String("    ", MN_X+20, MN_Y+25, RED, WHITE);
		lcd.cLCD_String(brktstr[c], MN_X+20, MN_Y+25, RED, WHITE);
	break;
	case D_K:
        delay(t1);
	if (c>0) c--;
		lcd.cLCD_String("    ", MN_X+20, MN_Y+25, RED, WHITE);
		lcd.cLCD_String(brktstr[c], MN_X+20, MN_Y+25, RED, WHITE);
	break;
	case L_K:
        delay(t1);
        menu_bracketstep();
        case R_K:
        delay(t1);
	loop();
	}
	}
	}
        }
}

// start bracket shooting
void bracket(){
	int ev;
        s = 0;     
	lcd.cLCD_Box(0,0, 131,131, FILL, WHITE);      // clear screen
      	lcd.cLCD_String("Shot #:", MN_X, MN_Y+10, BLACK, WHITE);
	lcd.cLCD_String("        ", MN_X+75, MN_Y+10, RED, WHITE);
      	lcd.cLCD_String("wait next:", MN_X, MN_Y+20, BLACK, WHITE);
	lcd.cLCD_String("         ", MN_X+75, MN_Y+20, RED, WHITE);
	lcd.cLCD_String("Exposing:", MN_X, MN_Y+35, BLACK, WHITE);
	lcd.cLCD_String("      ", MN_X+20, MN_Y+45, RED, WHITE);   // clear
	lcd.cLCD_String(expstr[b], MN_X+20, MN_Y+45, RED, WHITE);  // print current exposure time, here the base EV
	lcd.cLCD_String("Millisecs:", MN_X, MN_Y+65, BLACK, WHITE);
        lcd.cLCD_String("          ", MN_X+75, MN_Y+65, RED, WHITE);
//     	lcd.cLCD_String(ultoa(exptime[b], tmpstr0, 10), MN_X+75, MN_Y+65, RED, WHITE);
       	lcd.cLCD_String(ultoa(exptime[b], tmpstr, 10), MN_X+75, MN_Y+65, RED, WHITE);
        lcd.cLCD_String("Register:", MN_X, MN_Y+75, BLACK, WHITE);
        ev = b;
        lcd.cLCD_String(itoa(ev, tmpstr, 10), MN_X+75, MN_Y+75, RED, WHITE);    //print register# in array, 1st instance appears in 'bracket dance'
        s = s + 1;    //counting the shots taken
	lcd.cLCD_String(itoa(s, tmpstr, 10), MN_X+75, MN_Y+10, RED, WHITE);	// print # of shot in sequence
	fire(exptime[b]);    // take the centershot

	// Do the EV Bracketing
        ev = b+(((n-1)/2)*w);  //EV max. / bracketing on the long side
        for (byte i=(n-1)/2; i>=1; i--){
                s = s + 1;    //counting the shots taken
	        lcd.cLCD_String(itoa(s, tmpstr, 10), MN_X+75, MN_Y+10, RED, WHITE);	// print # of shot in sequence
		lcd.cLCD_String("      ", MN_X+20, MN_Y+45, RED, WHITE); // clear
		lcd.cLCD_String(expstr[ev], MN_X+20, MN_Y+45, RED, WHITE); // print current exposure time
        	lcd.cLCD_String("       ", MN_X+75, MN_Y+65, RED, WHITE);
        	lcd.cLCD_String(ultoa(exptime[ev], tmpstr, 10), MN_X+75, MN_Y+65, RED, WHITE);  // print exposure in milliseconds
                lcd.cLCD_String("         ", MN_X+75, MN_Y+75, RED, WHITE);
                lcd.cLCD_String(itoa(ev, tmpstr, 10), MN_X+75, MN_Y+75, RED, WHITE);    //print register# in array
      	fire(exptime[ev]);
        ev=ev-w;
        }

        ev = b-(((n-1)/2)*w);  //EV min. / bracketing on the short side
        for (byte i=(n-1)/2; i>=1; i--){        
                s = s + 1;    //counting the shots taken
	        lcd.cLCD_String(itoa(s, tmpstr, 10), MN_X+75, MN_Y+10, RED, WHITE);	// print # of shot in sequence
		lcd.cLCD_String("      ", MN_X+20, MN_Y+45, RED, WHITE); // clear
		lcd.cLCD_String(expstr[ev], MN_X+20, MN_Y+45, RED, WHITE); // print current exposure time
        	lcd.cLCD_String("       ", MN_X+75, MN_Y+65, RED, WHITE);
        	lcd.cLCD_String(ultoa(exptime[ev], tmpstr, 10), MN_X+75, MN_Y+65, RED, WHITE);  // print exposure in milliseconds
                lcd.cLCD_String("         ", MN_X+75, MN_Y+75, RED, WHITE);
                lcd.cLCD_String(itoa(ev, tmpstr, 10), MN_X+75, MN_Y+75, RED, WHITE);    //print register# in array
      	fire(exptime[ev]);
        ev=ev+w;
        }
// end EV Bracketing (shots are in the box)
	main_menu();
}
// The shutter relase function. May be adjusted for specific camera model.
// open shutter for 't' milliseconds
void fire(unsigned long t){
	pinMode(FOCUS, OUTPUT);			// outpin 6 gives output
	pinMode(SHUTTERPIN, OUTPUT);		// outpin 4 gives output
	digitalWrite(FOCUS, HIGH);		// initiate focus and camera ready ('r6' for the Nikon D300)
	delay(INITfocus);			// give time till camera 'beeps' the ready signal
	digitalWrite(SHUTTERPIN, HIGH);		// initiate exposure, activate mirror-up / on Nikon D300 set d9=on, results in a 600ms delay till the actual exposure starts
	delay(INITfire);			// there must be a short delay so the circuit has just enough time to trigger the shutter release
	digitalWrite(SHUTTERPIN, LOW);		// mirror is up and shutter has been released, the Shutterpin has no more effect can be set to LOW
	delay(t);				// this is the actual exposure 
	digitalWrite(FOCUS, LOW);		// end the exposure delayed by 't'

/*
        lcd.cLCD_String(brktstr[c], MN_X+75, MN_Y+20, RED, WHITE);    // *ON Noise-Reduction ON
	delay(brktwait[c]);    // *ON Noise-Reduction ON - delay for as long as the last exposure till the next shot
*/
        lcd.cLCD_String(brktstr[c], MN_X+75, MN_Y+20, RED, WHITE);	// **OFF Noise-Reduction OFF
	delay(brktwait[c]);						// **OFF Noise-Reduction OFF - delay for as per selection in menu
}

/*
The following are interrupt-driven keypad reading 
which includes DEBOUNCE ON/OFF mechanism, and continuous pressing detection
Best is to leave as is
*/
// Convert ADC value to key number
char get_key(unsigned int input)
{
	char k;
	for (k = 0; k < NUM_KEYS; k++)
	{
		if (input < adc_key_val[k])
		{
    return k;
        }
	}
    if (k >= NUM_KEYS)
        k = -1;     // No valid key pressed
    return k;
}

void update_adc_key(){
  int adc_key_in;
  char key_in;
  byte i;
  adc_key_in = analogRead(0);
  key_in = get_key(adc_key_in);
  for(i=0; i<NUM_KEYS; i++)
  {
    if(key_in==i)  //one key is pressed 
    { 
      if(button_count[i]<DEBOUNCE_MAX)
      {
        button_count[i]++;
        if(button_count[i]>DEBOUNCE_ON)
        {
          if(button_status[i] == 0)
          {
            button_flag[i] = 1;
            button_status[i] = 1; //button debounced to 'pressed' status
          }
        }
      }
    }
    else // no button pressed
    {
      if (button_count[i] >0)
      {  
		button_flag[i] = 0;	
		button_count[i]--;
        if(button_count[i]<DEBOUNCE_OFF){
          button_status[i]=0;   //button debounced to 'released' status
        }
      }
    }
  }
}

// Timer2 interrupt routine -
// 1/(160000000/256/(256-6)) = 4ms interval
ISR(TIMER2_OVF_vect) {  
  TCNT2  = 6;
  update_adc_key();
}

contact HALO-Photographs