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.
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.
/* 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(); }