2 * Arduino Intervalometer
6 enum {INTERVAL, BULB, INTERVALBULB, TRIGGER};
12 // Running/Shutter LEDs (through digital pots)
13 // Contrast adjustment (through digital pots/encoder)
15 // Speed up encoder more, later in the 'minute' range
16 // Hour representation?
17 // Either need to support more digits, or arbitrarily limit durations
18 // Test battery life - provide timeouts for display backlight? can atmega /sleep/?
19 // Fade up backlight/contrast on init!
27 int triggerInput = 2; // Feedback for trigger reset
29 int encoderButton = 3; // Encoder pushbutton
30 int buttonA = 4; // Left pushbutton
31 int buttonB = 5; // Right pushbutton
35 int cameraShutter = 0; // Pulse low for shutter release
37 int encoderPinA = 2; // HW interrupt
38 int encoderPinB = 3; // HW interrupt
41 int lcdDataBus[] = { 4, 5, 6, 7 };
45 int triggerReset = 12;
47 int potSelect = 10; // SPI (SS) for digital pots
48 int potData = 11; // SPI (MOSI) for digital pots
49 int potClock = 13; // SPI (SCK) for digital pots
51 /////////////////////////////
53 int currentMode = INTERVAL, selected = 0;
55 // Debouncing time variables
56 unsigned long lastToggleRunning = 0;
57 unsigned long lastModeUpdate = 0;
58 unsigned long lastSelectedUpdate = 0;
60 unsigned long lastShutter = 0;
62 int updateHeader = 1, updateEncoder = 1;
65 // Exposure and Timelapse durations
66 volatile long exposureTime = 0, lapseTime = 0;
67 int exposureRepresentation = 0, lapseRepresentation = 0;
70 * Higher level hardware functions
73 void pulsePin(int pin, int value)
75 digitalWrite(pin, !value);
77 digitalWrite(pin, value);
79 digitalWrite(pin, !value);
83 void parallelShiftOut(int firstPin, int lastPin, int value)
86 for(i = firstPin; i <= lastPin; i++)
88 digitalWrite(i, value & 0x01);
94 * LCD Control Functions
97 void lcdCommandWrite(int value)
100 int value1 = value >> 4;
102 parallelShiftOut(lcdDataBus[0], lcdDataInt, value1);
104 pulsePin(lcdEnable, HIGH);
106 parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value);
109 parallelShiftOut(lcdDataClock, lcdDataInt, value);
111 pulsePin(lcdEnable, HIGH);
114 void lcdDataWrite(int value)
117 int value1 = value >> 4;
119 digitalWrite(lcdDataInt, HIGH);
120 digitalWrite(lcdDataClock, LOW);
122 parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value1);
124 pulsePin(lcdEnable, HIGH);
126 digitalWrite(lcdDataInt, HIGH);
127 digitalWrite(lcdDataClock, LOW);
129 parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value);
131 pulsePin(lcdEnable, HIGH);
134 void lcdNumberWrite(int nr)
137 int n2 = (nr - n1 * 100) / 10;
140 lcdCommandWrite(560 + n2);
142 nr = nr - n1 * 100 - n2 * 10;
143 lcdCommandWrite(560 + nr);
146 void lcdHome(int row)
148 lcdCommandWrite(0x02);
153 lcdCommandWrite(0xC0);
160 lcdCommandWrite(0x01);
165 * Hardware initialization functions
172 for (i = lcdEnable; i <= lcdDataInt; i++)
175 // there's a chance that when we drop to not running
176 // on top of the arduino bootloader, we'll need a somewhat
177 // significant delay here (called for in the spec, but the bl is slow)
179 lcdCommandWrite(0x03); delay(64);
180 lcdCommandWrite(0x03); delay(50);
181 lcdCommandWrite(0x03); delay(50);
182 lcdCommandWrite(0x02); delay(50);
183 lcdCommandWrite(0x2C); delay(20);
184 lcdCommandWrite(0x06); delay(20);
185 lcdCommandWrite(0x0C); delay(20);
186 lcdCommandWrite(0x01); delay(100);
187 lcdCommandWrite(0x80); delay(30);
192 pinMode(encoderPinA, INPUT);
193 digitalWrite(encoderPinA, HIGH);
194 pinMode(encoderPinB, INPUT);
195 digitalWrite(encoderPinB, HIGH);
197 attachInterrupt(0, doEncoderA, CHANGE);
198 attachInterrupt(1, doEncoderB, CHANGE);
203 pinMode(cameraShutter, OUTPUT);
204 digitalWrite(cameraShutter, HIGH);
206 pinMode(triggerReset, OUTPUT);
207 // keep the reset low except when in trigger mode, so we don't accidentally trigger!
208 digitalWrite(triggerReset, LOW);
214 void incrementValue()
218 if(lapseRepresentation == 0)
225 if(exposureRepresentation == 0)
230 if(exposureTime > lapseTime)
231 lapseTime = exposureTime;
235 void decrementValue()
239 if(lapseRepresentation == 0 || lapseTime == 60) // careful around transition; thanks, nate
249 if(exposureRepresentation == 0 || exposureTime == 60)
259 void updateTimeRepresentations()
262 lapseRepresentation = 1;
264 lapseRepresentation = 0;
266 if(exposureTime >= 60)
267 exposureRepresentation = 1;
269 exposureRepresentation = 0;
280 delayMicroseconds(3000); // maximum bounce time, accd. to spec.
282 if (digitalRead(encoderPinA) == HIGH)
284 if (digitalRead(encoderPinB) == LOW)
291 if (digitalRead(encoderPinB) == HIGH)
297 updateTimeRepresentations();
309 delayMicroseconds(3000);
310 if (digitalRead(encoderPinB) == HIGH)
312 if (digitalRead(encoderPinA) == HIGH)
319 if (digitalRead(encoderPinA) == LOW)
325 updateTimeRepresentations();
333 unsigned long diff = (millis() - lastModeUpdate);
335 if(diff < 300) // careful about the overflow...
337 lastModeUpdate = millis();
341 lastModeUpdate = millis();
347 if(currentMode == BULB)
352 if(currentMode == TRIGGER)
353 digitalWrite(triggerReset, HIGH);
355 digitalWrite(triggerReset, LOW);
361 void switchSelected()
363 if(currentMode == INTERVALBULB)
365 unsigned long diff = (millis() - lastSelectedUpdate);
367 if(diff < 300) // careful about the overflow...
369 lastSelectedUpdate = millis();
373 lastSelectedUpdate = millis();
375 selected = !selected;
381 char runningIndicators[2] = { '*', '+' };
382 char oldRunningIndicator;
384 void updateRunningIndicator()
386 char i = ((millis() / 250) % 2);
388 if(i == oldRunningIndicator)
392 lcdCommandWrite(0xC8);
395 lcdDataWrite(runningIndicators[i]);
399 oldRunningIndicator = i;
404 unsigned long diff = (millis() - lastToggleRunning);
406 if(diff < 300) // careful about the overflow...
408 lastToggleRunning = millis();
412 lastToggleRunning = millis();
419 void drawTimecode(int secs, int rep)
423 lcdNumberWrite(secs);
428 lcdNumberWrite(secs/60);
433 int timecodeLength(int secs, int rep)
441 int n2 = (secs - n1 * 100) / 10;
449 char *modeHeader[4] = {"Interval", " Bulb", "Interval Bulb", "Trigger"};
458 for (count = 0; modeHeader[currentMode][count] != 0; count++)
459 lcdDataWrite(modeHeader[currentMode][count]);
460 for (; count < 16; count++)
471 if(currentMode != BULB)
472 drawTimecode(lapseTime, lapseRepresentation);
476 if(currentMode != BULB)
477 width += timecodeLength(lapseTime, lapseRepresentation);
478 if(currentMode != INTERVAL && currentMode != TRIGGER)
479 width += timecodeLength(exposureTime, exposureRepresentation);
483 lcdDataWrite(' '); width++;
484 lcdDataWrite(127); width++;
491 for(int i = 16; i > width; i--)
500 if(currentMode != INTERVAL && currentMode != TRIGGER)
501 drawTimecode(exposureTime, exposureRepresentation);
503 updateRunningIndicator();
508 if(analogRead(buttonA) == 0)
513 updateRunningIndicator();
515 if(currentMode == TRIGGER)
517 if(analogRead(triggerInput) < 100) // 100 might change with different resistors, make sure it works!
519 digitalWrite(triggerReset, HIGH);
520 delay(100); // this should probably be at least the time of the delay from signal (in the 555)...
521 digitalWrite(triggerReset, LOW);
523 digitalWrite(triggerReset, HIGH);
529 unsigned long diff = millis() - lastShutter;
531 int adjustedLapseTime = lapseTime;
532 if(currentMode != INTERVAL)
533 adjustedLapseTime -= exposureTime;
535 if(diff > (adjustedLapseTime * 1000)) // careful about the overflow...
537 digitalWrite(cameraShutter, LOW);
539 if(currentMode == INTERVAL)
542 delay(exposureTime * 1000); //delay for length of exposure
543 // biggest problem with this is that you can't stop a bulb (of either type)
544 // in the middle... you have to power off the intervalometer; same as you would have
545 // to do with a camera, I suppose, so people might be used to it.
546 // however, we can get around this by looping and checking millis()...
548 digitalWrite(cameraShutter, HIGH);
549 lastShutter = millis();
551 if(currentMode == BULB)
557 if(analogRead(buttonB) == 0)
560 if(analogRead(encoderButton) == 0)
563 updateRunningIndicator();