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!
20 // Fix when trigger is triggered and someone (ROBB) stops it.
21 // Why does trigger reset not get put back when we're done triggering!?
29 int triggerInput = 2; // Feedback for trigger reset
31 int encoderButton = 3; // Encoder pushbutton
32 int buttonA = 4; // Left pushbutton
33 int buttonB = 5; // Right pushbutton
37 int cameraShutter = 0; // Pulse low for shutter release
39 int encoderPinA = 2; // HW interrupt
40 int encoderPinB = 3; // HW interrupt
43 int lcdDataBus[] = { 4, 5, 6, 7 };
47 int triggerReset = 12;
49 int potSelect = 10; // SPI (SS) for digital pots
50 int potData = 11; // SPI (MOSI) for digital pots
51 int potClock = 13; // SPI (SCK) for digital pots
53 /////////////////////////////
55 int currentMode = INTERVAL, selected = 0;
57 // Debouncing time variables
58 unsigned long lastToggleRunning = 0;
59 unsigned long lastModeUpdate = 0;
60 unsigned long lastSelectedUpdate = 0;
62 unsigned long lastShutter = 0;
64 int updateHeader = 1, updateEncoder = 1;
67 // Exposure and Timelapse durations
68 volatile long exposureTime = 0, lapseTime = 0;
69 int exposureRepresentation = 0, lapseRepresentation = 0;
72 * Higher level hardware functions
75 void pulsePin(int pin, int value)
77 digitalWrite(pin, !value);
79 digitalWrite(pin, value);
81 digitalWrite(pin, !value);
85 void parallelShiftOut(int firstPin, int lastPin, int value)
88 for(i = firstPin; i <= lastPin; i++)
90 digitalWrite(i, value & 0x01);
96 * LCD Control Functions
99 void lcdCommandWrite(int value)
102 int value1 = value >> 4;
104 parallelShiftOut(lcdDataBus[0], lcdDataInt, value1);
106 pulsePin(lcdEnable, HIGH);
108 parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value);
111 parallelShiftOut(lcdDataClock, lcdDataInt, value);
113 pulsePin(lcdEnable, HIGH);
116 void lcdDataWrite(int value)
119 int value1 = value >> 4;
121 digitalWrite(lcdDataInt, HIGH);
122 digitalWrite(lcdDataClock, LOW);
124 parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value1);
126 pulsePin(lcdEnable, HIGH);
128 digitalWrite(lcdDataInt, HIGH);
129 digitalWrite(lcdDataClock, LOW);
131 parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value);
133 pulsePin(lcdEnable, HIGH);
136 void lcdNumberWrite(int nr)
139 int n2 = (nr - n1 * 100) / 10;
142 lcdCommandWrite(560 + n2);
144 nr = nr - n1 * 100 - n2 * 10;
145 lcdCommandWrite(560 + nr);
148 void lcdHome(int row)
150 lcdCommandWrite(0x02);
155 lcdCommandWrite(0xC0);
162 lcdCommandWrite(0x01);
167 * Hardware initialization functions
174 for (i = lcdEnable; i <= lcdDataInt; i++)
177 // there's a chance that when we drop to not running
178 // on top of the arduino bootloader, we'll need a somewhat
179 // significant delay here (called for in the spec, but the bl is slow)
181 lcdCommandWrite(0x03); delay(64);
182 lcdCommandWrite(0x03); delay(50);
183 lcdCommandWrite(0x03); delay(50);
184 lcdCommandWrite(0x02); delay(50);
185 lcdCommandWrite(0x2C); delay(20);
186 lcdCommandWrite(0x06); delay(20);
187 lcdCommandWrite(0x0C); delay(20);
188 lcdCommandWrite(0x01); delay(100);
189 lcdCommandWrite(0x80); delay(30);
194 pinMode(encoderPinA, INPUT);
195 digitalWrite(encoderPinA, HIGH);
196 pinMode(encoderPinB, INPUT);
197 digitalWrite(encoderPinB, HIGH);
199 attachInterrupt(0, doEncoderA, CHANGE);
200 attachInterrupt(1, doEncoderB, CHANGE);
205 pinMode(2N3904, OUTPUT);
206 digitalWrite(cameraShutter, HIGH);
208 pinMode(triggerReset, OUTPUT);
209 // keep the reset low except when in trigger mode, so we don't accidentally trigger!
210 digitalWrite(triggerReset, LOW);
216 void incrementValue()
220 if(lapseRepresentation == 0)
227 if(exposureRepresentation == 0)
232 if(exposureTime > lapseTime)
233 lapseTime = exposureTime;
237 void decrementValue()
241 if(lapseRepresentation == 0 || lapseTime == 60) // careful around transition; thanks, nate
251 if(exposureRepresentation == 0 || exposureTime == 60)
261 void updateTimeRepresentations()
264 lapseRepresentation = 1;
266 lapseRepresentation = 0;
268 if(exposureTime >= 60)
269 exposureRepresentation = 1;
271 exposureRepresentation = 0;
282 delayMicroseconds(3000); // maximum bounce time, accd. to spec.
284 if (digitalRead(encoderPinA) == HIGH)
286 if (digitalRead(encoderPinB) == LOW)
293 if (digitalRead(encoderPinB) == HIGH)
299 updateTimeRepresentations();
311 delayMicroseconds(3000);
312 if (digitalRead(encoderPinB) == HIGH)
314 if (digitalRead(encoderPinA) == HIGH)
321 if (digitalRead(encoderPinA) == LOW)
327 updateTimeRepresentations();
335 unsigned long diff = (millis() - lastModeUpdate);
337 if(diff < 300) // careful about the overflow...
339 lastModeUpdate = millis();
343 lastModeUpdate = millis();
349 if(currentMode == BULB)
354 if(currentMode == TRIGGER)
355 digitalWrite(triggerReset, HIGH);
357 digitalWrite(triggerReset, LOW);
363 void switchSelected()
365 if(currentMode == INTERVALBULB)
367 unsigned long diff = (millis() - lastSelectedUpdate);
369 if(diff < 300) // careful about the overflow...
371 lastSelectedUpdate = millis();
375 lastSelectedUpdate = millis();
377 selected = !selected;
383 char runningIndicators[2] = { '*', '+' };
384 char oldRunningIndicator;
386 void updateRunningIndicator()
388 char i = ((millis() / 250) % 2);
390 if(i == oldRunningIndicator)
394 lcdCommandWrite(0xC8);
397 lcdDataWrite(runningIndicators[i]);
401 oldRunningIndicator = i;
406 unsigned long diff = (millis() - lastToggleRunning);
408 if(diff < 300) // careful about the overflow...
410 lastToggleRunning = millis();
414 lastToggleRunning = millis();
421 void drawTimecode(int secs, int rep)
425 lcdNumberWrite(secs);
430 lcdNumberWrite(secs/60);
435 int timecodeLength(int secs, int rep)
443 int n2 = (secs - n1 * 100) / 10;
451 char *modeHeader[4] = {"Interval", " Bulb", "Interval Bulb", "Trigger"};
460 for (count = 0; modeHeader[currentMode][count] != 0; count++)
461 lcdDataWrite(modeHeader[currentMode][count]);
462 for (; count < 16; count++)
473 if(currentMode != BULB)
474 drawTimecode(lapseTime, lapseRepresentation);
478 if(currentMode != BULB)
479 width += timecodeLength(lapseTime, lapseRepresentation);
480 if(currentMode != INTERVAL && currentMode != TRIGGER)
481 width += timecodeLength(exposureTime, exposureRepresentation);
485 lcdDataWrite(' '); width++;
486 lcdDataWrite(127); width++;
493 for(int i = 16; i > width; i--)
502 if(currentMode != INTERVAL && currentMode != TRIGGER)
503 drawTimecode(exposureTime, exposureRepresentation);
505 updateRunningIndicator();
510 if(analogRead(buttonA) == 0)
515 updateRunningIndicator();
517 if(currentMode == TRIGGER)
519 if(analogRead(triggerInput) < 100) // 100 might change with different resistors, make sure it works!
521 digitalWrite(triggerReset, HIGH);
522 delay(100); // this should probably be at least the time of the delay from signal (in the 555)...
523 digitalWrite(triggerReset, LOW);
525 digitalWrite(triggerReset, HIGH);
531 unsigned long diff = millis() - lastShutter;
533 int adjustedLapseTime = lapseTime;
534 if(currentMode != INTERVAL)
535 adjustedLapseTime -= exposureTime;
537 if(diff > (adjustedLapseTime * 1000)) // careful about the overflow...
539 digitalWrite(cameraShutter, LOW);
541 if(currentMode == INTERVAL)
544 delay(exposureTime * 1000); //delay for length of exposure
545 // biggest problem with this is that you can't stop a bulb (of either type)
546 // in the middle... you have to power off the intervalometer; same as you would have
547 // to do with a camera, I suppose, so people might be used to it.
548 // however, we can get around this by looping and checking millis()...
550 digitalWrite(cameraShutter, HIGH);
551 lastShutter = millis();
553 if(currentMode == BULB)
559 if(analogRead(buttonB) == 0)
562 if(analogRead(encoderButton) == 0)
565 updateRunningIndicator();