Created
August 26, 2013 01:42
-
-
Save benjamingeiger/6337439 to your computer and use it in GitHub Desktop.
Intervalometer code that started breaking.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <LiquidCrystal.h> | |
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); | |
#include <EEPROM.h> | |
/* | |
** Hardware configuration. | |
*/ | |
#define MODE_PIN 2 | |
#define ENABLE_PIN 3 | |
#define INCREASE_PIN 4 | |
#define DECREASE_PIN 5 | |
#define SHUTTER_PIN 6 | |
#define SPAN_DEBOUNCE 50 | |
/* | |
** Define time intervals (in parallel arrays). | |
*/ | |
byte num_intervals = 50; | |
char *txt_interval_labels[] = { | |
// 0.25-1s, 0.25s | |
"1/4s", "1/2s", "3/4s", "1s", | |
// 2-10s, 1s | |
"2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s", "10s", | |
// 15-60s, 5s | |
"15s", "20s", "25s", "30s", "35s", "40s", "45s", "50s", "55s", "1m", | |
// 70s-2m, 10s | |
"1m10s", "1m20s", "1m30s", "1m40s", "1m50s", "2m", | |
// 2.5m-5m, 0.5m | |
"2m30s", "3m", "3m30s", "4m", "4m30s", "5m", | |
// 6m-10m, 1m | |
"6m", "7m", "8m", "9m", "10m", | |
// 15m-1h, 5m | |
"15m", "20m", "25m", "30m", "35m", | |
"40m", "45m", "50m", "55m", "1h" | |
}; | |
unsigned long span_intervals[] = { | |
// 0.25-1s, 0.25s | |
250, 500, 750, 1000, | |
// 2-10s, 1s | |
2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, | |
// 15-60s, 5s | |
15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 55000, 60000, | |
// 70s-2m, 10s | |
70000, 80000, 90000, 100000, 110000, 120000, | |
// 2.5m-5m, 0.5m | |
150000, 180000, 210000, 240000, 270000, 300000, | |
// 6m-10m, 1m | |
360000, 420000, 480000, 540000, 600000, | |
// 15m-1h, 5m | |
900000, 1200000, 1500000, 1800000, 2100000, | |
2400000, 2700000, 3000000, 3300000, 3600000 | |
}; | |
/* | |
** Configuration options (set through UI). | |
*/ | |
signed char idx_duration = 1; // Default to 0.5s | |
signed char idx_interval = 7; // Default to 5s | |
boolean use_mirror_lockup = true; // Default to true | |
// When locking the mirror: | |
// How long should the shutter button be held? | |
#define MLU_DURATION 100 | |
// How long should we wait before taking the photo? | |
#define MLU_INTERVAL 400 | |
// Screw enums. Arduino hates 'em. | |
char current_state; | |
/* | |
** State definitions. | |
*/ | |
#define READY 0 | |
#define TIMING 10 | |
#define MIRROR_LOCKING 20 | |
#define MIRROR_LOCKED 30 | |
#define SHOOTING 40 | |
#define SET_INTERVAL 50 | |
#define SET_DURATION 60 | |
#define SET_LOCKUP 70 | |
unsigned long time_last_change = 0; | |
// Switch to the given state. | |
static void switch_to_state(char state) | |
{ | |
current_state = state; | |
time_last_change = millis(); | |
lcd.clear(); | |
//Serial.print("Switching to state "); | |
//Serial.print((int) state); | |
//Serial.print("\n"); | |
} | |
static unsigned long time_since_last_state_change() | |
{ | |
return millis() - time_last_change; | |
} | |
/*********************************************************************** | |
MAIN STATE BODIES | |
***********************************************************************/ | |
static void do_ready() | |
{ | |
lcd_update_mode("Ready"); | |
lcd_clear_time(); | |
if (is_enable_on()) { | |
//switch_to_state(TIMING); | |
if (use_mirror_lockup) { | |
switch_to_state(MIRROR_LOCKING); | |
} else { | |
switch_to_state(SHOOTING); | |
} | |
return; | |
} | |
if (was_mode_pressed()) { | |
switch_to_state(SET_INTERVAL); | |
return; | |
} | |
} | |
static void do_timing() | |
{ | |
lcd_update_mode("Timing"); | |
lcd_update_time(span_intervals[idx_interval] - time_since_last_state_change()); | |
if (! is_enable_on()) { | |
switch_to_state(READY); | |
return; | |
} | |
if (time_since_last_state_change() > span_intervals[idx_interval]) { | |
if (use_mirror_lockup) { | |
switch_to_state(MIRROR_LOCKING); | |
} else { | |
switch_to_state(SHOOTING); | |
} | |
} | |
} | |
static void do_shooting() | |
{ | |
lcd_update_mode("Shooting"); | |
lcd_update_time(span_intervals[idx_duration] - time_since_last_state_change()); | |
if (is_shutter_open) { | |
if (time_since_last_state_change() > span_intervals[idx_duration]) { | |
close_shutter(); | |
switch_to_state(TIMING); | |
} | |
} else { | |
open_shutter(); | |
} | |
} | |
static void do_mirror_locking() | |
{ | |
lcd_update_mode("Locking"); | |
lcd_clear_time(); | |
if (is_shutter_open) { | |
if (time_since_last_state_change() > MLU_DURATION) { | |
close_shutter(); | |
switch_to_state(MIRROR_LOCKED); | |
} | |
} else { | |
open_shutter(); | |
} | |
} | |
static void do_mirror_locked() | |
{ | |
lcd_update_mode("Locked"); | |
lcd_clear_time(); | |
if (time_since_last_state_change() > MLU_INTERVAL) { | |
switch_to_state(SHOOTING); | |
} | |
} | |
static void do_set_interval() | |
{ | |
lcd_update_mode("Interval"); | |
lcd_clear_time(); | |
if (was_mode_pressed()) { | |
EEPROM.write(0, idx_interval); | |
switch_to_state(SET_DURATION); | |
return; | |
} | |
if (was_increase_pressed()) { | |
idx_interval++; | |
idx_interval %= num_intervals; | |
} | |
if (was_decrease_pressed()) { | |
idx_interval--; | |
if (idx_interval < 0) idx_interval += num_intervals; | |
} | |
} | |
static void do_set_duration() | |
{ | |
lcd_update_mode("Duration"); | |
lcd_clear_time(); | |
if (was_mode_pressed()) { | |
EEPROM.write(1, idx_duration); | |
switch_to_state(SET_LOCKUP); | |
return; | |
} | |
if (was_increase_pressed()) { | |
idx_duration++; | |
idx_duration %= num_intervals; | |
} | |
if (was_decrease_pressed()) { | |
idx_duration--; | |
if (idx_duration < 0) idx_duration += num_intervals; | |
Serial.print(idx_duration); | |
} | |
} | |
static void do_set_lockup() | |
{ | |
lcd_update_mode("Lockup"); | |
lcd_clear_time(); | |
if (was_mode_pressed()) { | |
EEPROM.write(2, use_mirror_lockup); | |
switch_to_state(READY); | |
return; | |
} | |
if (was_increase_pressed() || was_decrease_pressed()) { | |
use_mirror_lockup = !use_mirror_lockup; | |
} | |
} | |
/* | |
** Utility methods to read input state. | |
*/ | |
// Read ENABLE state. (ENABLE should be a switch, not a button.) | |
static boolean is_enable_on() | |
{ | |
int enable = digitalRead(ENABLE_PIN); | |
return (enable == LOW); | |
} | |
int mode_current = LOW; | |
int mode_previous = LOW; | |
unsigned long time_mode_down; | |
boolean mode_triggered = false; | |
// Check whether MODE was pressed. | |
static boolean was_mode_pressed() | |
{ | |
mode_previous = mode_current; | |
mode_current = digitalRead(MODE_PIN); | |
//return ((mode_current == HIGH) && (mode_previous == LOW)); | |
if (mode_current == LOW) { | |
if (mode_previous == HIGH) { | |
time_mode_down = millis(); | |
return false; | |
} else if ((millis() - time_mode_down > SPAN_DEBOUNCE) | |
&& (! mode_triggered)) { | |
mode_triggered = true; | |
return true; | |
} else return false; | |
} else { | |
mode_triggered = false; | |
return false; | |
} | |
} | |
int increase_current = LOW; | |
int increase_previous = LOW; | |
unsigned long time_increase_down; | |
boolean increase_triggered = false; | |
// Check whether INCREASE was pressed. | |
static boolean was_increase_pressed() | |
{ | |
increase_previous = increase_current; | |
increase_current = digitalRead(INCREASE_PIN); | |
if (increase_current == LOW) { | |
if (increase_previous == HIGH) { | |
time_increase_down = millis(); | |
return false; | |
} else if ((millis() - time_increase_down > SPAN_DEBOUNCE) | |
&& (! increase_triggered)) { | |
increase_triggered = true; | |
return true; | |
} else return false; | |
} else { | |
increase_triggered = false; | |
return false; | |
} | |
} | |
int decrease_current = LOW; | |
int decrease_previous = LOW; | |
unsigned long time_decrease_down; | |
boolean decrease_triggered = false; | |
// Check whether DECREASE was pressed. | |
static boolean was_decrease_pressed() | |
{ | |
decrease_previous = decrease_current; | |
decrease_current = digitalRead(DECREASE_PIN); | |
if (decrease_current == LOW) { | |
if (decrease_previous == HIGH) { | |
time_decrease_down = millis(); | |
return false; | |
} else if ((millis() - time_decrease_down > SPAN_DEBOUNCE) | |
&& (! decrease_triggered)) { | |
decrease_triggered = true; | |
return true; | |
} else return false; | |
} else { | |
decrease_triggered = false; | |
return false; | |
} | |
} | |
boolean is_shutter_open = false; | |
static void open_shutter() | |
{ | |
digitalWrite(SHUTTER_PIN, HIGH); | |
is_shutter_open = true; | |
} | |
static void close_shutter() | |
{ | |
digitalWrite(SHUTTER_PIN, LOW); | |
is_shutter_open = false; | |
} | |
static void lcd_update_mode(char *text) | |
{ | |
static char output[9]; | |
strncpy(output, text, 8); | |
lcd.home(); | |
lcd.print(output); | |
// print enough spaces to blank the rest | |
lcd.print(" " + strlen(output)); | |
} | |
static void lcd_update_interval(int index) | |
{ | |
lcd.setCursor(0, 1); | |
lcd.print("I:"); | |
lcd.print(" " + strlen(txt_interval_labels[index])); | |
lcd.print(txt_interval_labels[index]); | |
lcd.print(" "); | |
} | |
static void lcd_update_duration(int index) | |
{ | |
lcd.setCursor(8, 1); | |
lcd.print("D:"); | |
lcd.print(" " + strlen(txt_interval_labels[index])); | |
lcd.print(txt_interval_labels[index]); | |
lcd.print(" "); | |
} | |
static void lcd_update_mirror_lockup(boolean lock) | |
{ | |
lcd.setCursor(15, 0); | |
lcd.print(lock ? "L" : " "); | |
} | |
static void lcd_update_time(unsigned long time) | |
{ | |
lcd.setCursor(9, 0); | |
unsigned int seconds = time / 1000 + 1; | |
unsigned int minutes = seconds / 60; | |
seconds = seconds % 60; | |
if (minutes < 10) lcd.print(" "); | |
if (minutes < 100) lcd.print(minutes); | |
else lcd.print("X"); | |
lcd.print(":"); | |
if (seconds < 10) lcd.print("0"); | |
lcd.print(seconds); | |
} | |
static void lcd_clear_time() | |
{ | |
lcd.setCursor(9, 0); | |
lcd.print(" "); | |
} | |
/* | |
** Setup method. | |
*/ | |
void setup() | |
{ | |
pinMode(SHUTTER_PIN, OUTPUT); | |
pinMode(MODE_PIN, INPUT); | |
pinMode(ENABLE_PIN, INPUT); | |
pinMode(INCREASE_PIN, INPUT); | |
pinMode(DECREASE_PIN, INPUT); | |
digitalWrite(MODE_PIN, HIGH); | |
digitalWrite(ENABLE_PIN, HIGH); | |
digitalWrite(INCREASE_PIN, HIGH); | |
digitalWrite(DECREASE_PIN, HIGH); | |
lcd.begin(16, 2); | |
Serial.begin(115200); | |
byte interval = EEPROM.read(0); | |
if ((interval < 0) || (interval > num_intervals)) { | |
idx_interval = 7; | |
} else { | |
idx_interval = interval; | |
} | |
byte duration = EEPROM.read(1); | |
if ((duration < 0) || (duration > num_intervals)) { | |
idx_duration = 1; | |
} else { | |
idx_duration = duration; | |
} | |
byte lockup = EEPROM.read(2); | |
if ((lockup < 0) || (lockup > 1)) { | |
use_mirror_lockup = false; | |
} else { | |
use_mirror_lockup = lockup; | |
} | |
current_state = READY; | |
lcd.clear(); | |
} | |
/* | |
** Main loop body. | |
*/ | |
void loop() | |
{ | |
lcd_update_interval(idx_interval); | |
lcd_update_duration(idx_duration); | |
lcd_update_mirror_lockup(use_mirror_lockup); | |
switch (current_state) { | |
case READY: | |
// not started yet | |
do_ready(); | |
break; | |
case TIMING: | |
// waiting for an exposure | |
do_timing(); | |
break; | |
case MIRROR_LOCKING: | |
do_mirror_locking(); | |
break; | |
case MIRROR_LOCKED: | |
do_mirror_locked(); | |
break; | |
case SHOOTING: | |
// shutter is open | |
do_shooting(); | |
break; | |
case SET_INTERVAL: | |
// setting interval | |
do_set_interval(); | |
break; | |
case SET_DURATION: | |
do_set_duration(); | |
break; | |
case SET_LOCKUP: | |
do_set_lockup(); | |
break; | |
default: | |
// DO SOMETHING! | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment