Fix schematic to match what I rebuilt today...
[arduino-intervalometer.git] / Intervalometer.pde
blob7ecdf4878303a77203773fae9bff5e51e58f1ba8
1 /*
2  *  Arduino Intervalometer
3  *  Tim Horton, 2008
4  */
6 enum {INTERVAL, BULB, INTERVALBULB, TRIGGER};
8 /*
9  *  TODOs
10  */
12 // Running/Shutter LEDs (through digital pots)
13 // Contrast adjustment (through digital pots/encoder)
14 // Easter egg!
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!?
24  *  Pin Assignment
25  */
27 // Analog Pins
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
35 // Digital Pins
37 int cameraShutter = 0; // Pulse low for shutter release
39 int encoderPinA =       2; // HW interrupt
40 int encoderPinB =       3; // HW interrupt
42 int lcdEnable =         1;
43 int lcdDataBus[] =      { 4, 5, 6, 7 };
44 int lcdDataClock =      8;
45 int lcdDataInt =        9;
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;
65 int running = 0;
67 // Exposure and Timelapse durations
68 volatile long exposureTime = 0, lapseTime = 0;
69 int exposureRepresentation = 0, lapseRepresentation = 0;
72  *  Higher level hardware functions
73  */
75 void pulsePin(int pin, int value)
77         digitalWrite(pin, !value);
78         delay(1);
79         digitalWrite(pin, value);
80         delay(1);
81         digitalWrite(pin, !value);
82         delay(1);
85 void parallelShiftOut(int firstPin, int lastPin, int value)
87         int i;
88         for(i = firstPin; i <= lastPin; i++)
89         {
90                 digitalWrite(i, value & 0x01);
91                 value >>= 1;
92         }
96  *  LCD Control Functions
97  */
99 void lcdCommandWrite(int value)
101         int i = 0;
102         int value1 = value >> 4;
103         
104         parallelShiftOut(lcdDataBus[0], lcdDataInt, value1);
105         
106         pulsePin(lcdEnable, HIGH);
108         parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value);
109         
110         value >>= 4;
111         parallelShiftOut(lcdDataClock, lcdDataInt, value);
112         
113         pulsePin(lcdEnable, HIGH);
116 void lcdDataWrite(int value)
118         int i = 0;
119         int value1 = value >> 4;
120         
121         digitalWrite(lcdDataInt, HIGH);
122         digitalWrite(lcdDataClock, LOW);
123         
124         parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value1);
125         
126         pulsePin(lcdEnable, HIGH);
127         
128         digitalWrite(lcdDataInt, HIGH);
129         digitalWrite(lcdDataClock, LOW);
130         
131         parallelShiftOut(lcdDataBus[0], lcdDataBus[3], value);
132         
133         pulsePin(lcdEnable, HIGH);
136 void lcdNumberWrite(int nr)
138         int n1 = nr/100;
139         int n2 = (nr - n1 * 100) / 10;
140         
141         if(n2)
142                 lcdCommandWrite(560 + n2);
143         
144         nr = nr - n1 * 100 - n2 * 10;
145         lcdCommandWrite(560 + nr);
148 void lcdHome(int row)
150         lcdCommandWrite(0x02);
151         
152         delay(4);
153         
154         if(row == 1)
155                 lcdCommandWrite(0xC0);
156                 
157         delay(1);
160 void lcdClear()
162         lcdCommandWrite(0x01);
163         delay(4);
167  *  Hardware initialization functions
168  */
170 void lcdInit()
172         int i = 0;
173         
174         for (i = lcdEnable; i <= lcdDataInt; i++)
175                 pinMode(i, OUTPUT);
176         
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);
192 void encoderInit()
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);
203 void setup (void)
205         pinMode(2N3904, OUTPUT);
206         digitalWrite(cameraShutter, HIGH);
207         
208         pinMode(triggerReset, OUTPUT);
209         // keep the reset low except when in trigger mode, so we don't accidentally trigger!
210         digitalWrite(triggerReset, LOW);
212         lcdInit();
213         encoderInit();
216 void incrementValue()
218         if(selected == 0)
219         {
220                 if(lapseRepresentation == 0)
221                         lapseTime++;
222                 else
223                         lapseTime += 60;
224         }
225         else
226         {
227                 if(exposureRepresentation == 0)
228                         exposureTime++;
229                 else
230                         exposureTime += 60;
231                 
232                 if(exposureTime > lapseTime)
233                         lapseTime = exposureTime;
234         }
237 void decrementValue()
239         if(selected == 0)
240         {
241                 if(lapseRepresentation == 0 || lapseTime == 60) // careful around transition; thanks, nate
242                         lapseTime--;
243                 else
244                         lapseTime -= 60;
245                 
246                 if(lapseTime < 0)
247                         lapseTime = 0;
248         }
249         else
250         {
251                 if(exposureRepresentation == 0 || exposureTime == 60)
252                         exposureTime--;
253                 else
254                         exposureTime -= 60;
255                 
256                 if(exposureTime < 0)
257                         exposureTime = 0;
258         }
261 void updateTimeRepresentations()
263         if(lapseTime >= 60)
264                 lapseRepresentation = 1;
265         else
266                 lapseRepresentation = 0;
268         if(exposureTime >= 60)
269                 exposureRepresentation = 1;
270         else
271                 exposureRepresentation = 0;
272         
273         updateEncoder = 1;
276 void doEncoderA()
278         if(running)
279                 return;
280                 
281         noInterrupts();
282         delayMicroseconds(3000); // maximum bounce time, accd. to spec.
284         if (digitalRead(encoderPinA) == HIGH)
285         { 
286                 if (digitalRead(encoderPinB) == LOW)
287                         incrementValue();
288                 else
289                         decrementValue();
290         }
291         else                                       
292         {
293                 if (digitalRead(encoderPinB) == HIGH)
294                         incrementValue();
295                 else
296                         decrementValue();
297         }
298         
299         updateTimeRepresentations();
300         
301         updateEncoder = 1;
302         interrupts();
305 void doEncoderB()
307         if(running)
308                 return;
309         
310         noInterrupts();
311         delayMicroseconds(3000);
312         if (digitalRead(encoderPinB) == HIGH)
313         {
314                 if (digitalRead(encoderPinA) == HIGH)
315                         incrementValue();
316                 else
317                         decrementValue();
318         }
319         else
320         {
321                 if (digitalRead(encoderPinA) == LOW)
322                         incrementValue();
323                 else
324                         decrementValue();
325         }
326         
327         updateTimeRepresentations();
328         
329         updateEncoder = 1;
330         interrupts();
333 void switchModes()
335         unsigned long diff = (millis() - lastModeUpdate);
336         
337         if(diff < 300) // careful about the overflow...
338         {
339                 lastModeUpdate = millis();
340                 return;
341         }
342         else
343                 lastModeUpdate = millis();
344         
345         currentMode++;
346         if(currentMode > 3)
347                 currentMode = 0;
348         
349         if(currentMode == BULB)
350                 selected = 1;
351         else
352                 selected = 0;
353         
354         if(currentMode == TRIGGER)
355                 digitalWrite(triggerReset, HIGH);
356         else
357                 digitalWrite(triggerReset, LOW);
358                 
359         
360         updateHeader = 1;
363 void switchSelected()
365         if(currentMode == INTERVALBULB)
366         {
367                 unsigned long diff = (millis() - lastSelectedUpdate);
368                 
369                 if(diff < 300) // careful about the overflow...
370                 {
371                         lastSelectedUpdate = millis();
372                         return;
373                 }
374                 else
375                         lastSelectedUpdate = millis();
377                 selected = !selected;
378         }
379         
380         updateEncoder = 1;
383 char runningIndicators[2] = { '*', '+' };
384 char oldRunningIndicator;
386 void updateRunningIndicator()
388         char i = ((millis() / 250) % 2);
389         
390         if(i == oldRunningIndicator)
391                 return;
392         
393         lcdHome(1);
394         lcdCommandWrite(0xC8);
395         
396         if(running)
397                 lcdDataWrite(runningIndicators[i]);
398         else
399                 lcdDataWrite(' ');
400         
401         oldRunningIndicator = i;
404 void toggleRunning()
406         unsigned long diff = (millis() - lastToggleRunning);
407         
408         if(diff < 300) // careful about the overflow...
409         {
410                 lastToggleRunning = millis();
411                 return;
412         }
413         else
414                 lastToggleRunning = millis();
415         
416         running = !running;
417         
418         updateEncoder = 1;
421 void drawTimecode(int secs, int rep)
423         if(rep == 0)
424         {
425                 lcdNumberWrite(secs);
426                 lcdDataWrite('"');
427         }
428         else
429         {
430                 lcdNumberWrite(secs/60);
431                 lcdDataWrite('\'');
432         }
435 int timecodeLength(int secs, int rep)
437         int count = 2;
438         
439         if(rep)
440                 secs = secs/60;
441         
442         int n1 = secs/100;
443         int n2 = (secs - n1 * 100) / 10;
445         if(n2)
446                 count++;
447         
448         return count;
451 char *modeHeader[4] = {"Interval", "            Bulb", "Interval    Bulb", "Trigger"};
453 void loop(void)
455         if(updateHeader)
456         {
457                 lcdHome(0);
458                 
459                 int count;
460                 for (count = 0; modeHeader[currentMode][count] != 0; count++)
461                         lcdDataWrite(modeHeader[currentMode][count]);
462                 for (; count < 16; count++)
463                         lcdDataWrite(' ');
464                 
465                 updateHeader = 0;
466                 updateEncoder = 1;
467         }
468                 
469         if(updateEncoder)
470         {       
471                 lcdHome(1);
472                 
473                 if(currentMode != BULB)
474                         drawTimecode(lapseTime, lapseRepresentation);
475                 
476                 int width = 0;
477                 
478                 if(currentMode != BULB)
479                         width += timecodeLength(lapseTime, lapseRepresentation);
480                 if(currentMode != INTERVAL && currentMode != TRIGGER)
481                         width += timecodeLength(exposureTime, exposureRepresentation);
482                         
483                 if(selected == 0)
484                 {
485                         lcdDataWrite(' '); width++;
486                         lcdDataWrite(127); width++;
487                 }
488                 else
489                 {
490                         width += 2;
491                 }
492                                 
493                 for(int i = 16; i > width; i--)
494                         lcdDataWrite(' ');
495                 
496                 if(selected == 1)
497                 {
498                         lcdDataWrite(126);
499                         lcdDataWrite(' ');
500                 }
501                 
502                 if(currentMode != INTERVAL && currentMode != TRIGGER)
503                         drawTimecode(exposureTime, exposureRepresentation);
505                 updateRunningIndicator();
506                 
507                 updateEncoder = 0;
508         }
509         
510         if(analogRead(buttonA) == 0)
511                 toggleRunning();
512         
513         if(running)
514         {
515                 updateRunningIndicator();
516                 
517                 if(currentMode == TRIGGER)
518                 {
519                         if(analogRead(triggerInput) < 100) // 100 might change with different resistors, make sure it works!
520                         {
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);
524                                 delay(10);
525                                 digitalWrite(triggerReset, HIGH);
526                         }
527                         
528                         return;
529                 }
530                 
531                 unsigned long diff = millis() - lastShutter;
532                 
533                 int adjustedLapseTime = lapseTime;
534                 if(currentMode != INTERVAL)
535                         adjustedLapseTime -= exposureTime;
536                 
537                 if(diff > (adjustedLapseTime * 1000)) // careful about the overflow...
538                 {
539                         digitalWrite(cameraShutter, LOW);
540                         
541                         if(currentMode == INTERVAL)
542                                 delay(100);
543                         else
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()...
549                                 
550                         digitalWrite(cameraShutter, HIGH);
551                         lastShutter = millis();
552                         
553                         if(currentMode == BULB)
554                                 running = 0;
555                 }
556         }
557         else
558         {
559                 if(analogRead(buttonB) == 0)
560                         switchModes();
561                 
562                 if(analogRead(encoderButton) == 0)
563                         switchSelected();
564                 
565                 updateRunningIndicator();
566         }