Skip to content

Instantly share code, notes, and snippets.

@benjamingeiger
Created August 26, 2013 01:42
Show Gist options
  • Save benjamingeiger/6337439 to your computer and use it in GitHub Desktop.
Save benjamingeiger/6337439 to your computer and use it in GitHub Desktop.
Intervalometer code that started breaking.
#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