Finished schematic, maybe?
[arduino-intervalometer.git] / Intervalometer.pde
blob39b83667c5b9c5e8e1184b4e137787e0a4e0a15f
1 /*
2  *  Arduino Intervalometer
3  *  Tim Horton, 2008
4  */
6 enum {INTERVAL, BULB, INTERVALBULB, TRIGGER};
8 /*
9  *  Pin Assignment
10  */
12 // Analog Pins
14 int triggerInput =  2; // Feedback for trigger reset
16 int encoderButton =     3; // Encoder pushbutton
17 int buttonA =           4; // Left pushbutton
18 int buttonB =           5; // Right pushbutton
20 // Digital Pins
22 int cameraShutter = 0; // Pulse low for shutter release
24 int encoderPinA =       2; // HW interrupt
25 int encoderPinB =       3; // HW interrupt
27 int lcdEnable =         1;
28 int lcdDataBus[] =      { 4, 5, 6, 7 };
29 int lcdDataClock =      8;
30 int lcdDataInt =        9;
32 int triggerReset =  12;
34 int potSelect =         10; // SPI (SS) for digital pots
35 int potData =           11; // SPI (MOSI) for digital pots
36 int potClock =          13; // SPI (SCK) for digital pots
38 /////////////////////////////
40 int currentMode = INTERVAL, selected = 0;
41 int running = 0;
43 // Debouncing time variables
44 unsigned long lastToggleRunning = 0;
45 unsigned long lastModeUpdate = 0;
46 unsigned long lastSelectedUpdate = 0;
48 unsigned long lastShutter = 0;
50 int updateHeader = 1, updateEncoder = 1;
52 volatile long exposureTime = 0, lapseTime = 0;
53 int exposureRepresentation = 0, lapseRepresentation = 0;
56  *  Higher level hardware functions
57  */
59 void pulsePin(int pin, int value)
61         digitalWrite(pin, !value);
62         delay(1);
63         digitalWrite(pin, value);
64         delay(1);
65         digitalWrite(pin, !value);
66         delay(1);
70  *  LCD Control Functions
71  */
73 void lcdCommandWrite(int value)
75         int i = 0;
76         int value1 = value;
78         value1 >>= 4;
79         
80         for (i = lcdDataBus[0]; i <= lcdDataInt; i++)
81         {
82                 digitalWrite(i,value1 & 01);
83                 value1 >>= 1;
84         }
85         
86         pulsePin(lcdEnable, HIGH);
87         delay(1);
89         for (i = lcdDataBus[0]; i <= lcdDataBus[3]; i++)
90         {
91                 digitalWrite(i, value & 01);
92                 value >>= 1; 
93         }
94         
95         value >>= 4;
96         
97         for (i = lcdDataClock; i <= lcdDataInt; i++)
98         {
99                 digitalWrite(i, value & 01);
100                 value >>= 1;
101         }
102         
103         pulsePin(lcdEnable, HIGH);
106 void lcdDataWrite(int value)
108         int i = 0;
109         int value1 = value;
110         
111         digitalWrite(lcdDataInt, HIGH);
112         digitalWrite(lcdDataClock, LOW);
113         
114         value1 >>= 4;
115         
116         for (i=lcdDataBus[0]; i <= lcdDataBus[3]; i++)
117         {
118                 digitalWrite(i,value1 & 01);
119                 value1 >>= 1;
120         }
121         
122         pulsePin(lcdEnable, HIGH);
123         delay(1);
124         
125         digitalWrite(lcdDataInt, HIGH);
126         digitalWrite(lcdDataClock, LOW);
127         
128         for (i=lcdDataBus[0]; i <= lcdDataBus[3]; i++)
129         {
130                 digitalWrite(i,value & 01);
131                 value >>= 1;
132         }
133         
134         pulsePin(lcdEnable, HIGH);
137 void lcdNumberWrite(int nr)
139         int n1 = nr/100;
140         int n2 = (nr - n1 * 100) / 10;
141         
142         if(n2)
143                 lcdCommandWrite(560 + n2);
144         
145         nr = nr - n1 * 100 - n2 * 10;
146         lcdCommandWrite(560 + nr);
149 void lcdHome(int row)
151         lcdCommandWrite(0x02);
152         
153         delay(4);
154         
155         if(row == 1)
156                 lcdCommandWrite(0xC0);
157                 
158         delay(1);
161 void lcdClear()
163         lcdCommandWrite(0x01);
164         delay(4);
168  *  Hardware initialization functions
169  */
171 void lcdInit()
173         int i = 0;
174         
175         for (i = lcdEnable; i <= lcdDataInt; i++)
176                 pinMode(i, OUTPUT);
177         
178         // there's a chance that when we drop to not running 
179         // on top of the arduino bootloader, we'll need a somewhat
180         // significant delay here (called for in the spec, but the bl is slow)
182         lcdCommandWrite(0x03); delay(64);
183         lcdCommandWrite(0x03); delay(50);
184         lcdCommandWrite(0x03); delay(50);
185         lcdCommandWrite(0x02); delay(50);
186         lcdCommandWrite(0x2C); delay(20);
187         lcdCommandWrite(0x06); delay(20);
188         lcdCommandWrite(0x0C); delay(20);
189         lcdCommandWrite(0x01); delay(100);
190         lcdCommandWrite(0x80); delay(30);
193 void encoderInit()
195         pinMode(encoderPinA, INPUT); 
196         digitalWrite(encoderPinA, HIGH);
197         pinMode(encoderPinB, INPUT); 
198         digitalWrite(encoderPinB, HIGH);
200         attachInterrupt(0, doEncoderA, CHANGE);
201         attachInterrupt(1, doEncoderB, CHANGE);
204 void setup (void)
206         pinMode(cameraShutter, OUTPUT);
207         digitalWrite(cameraShutter, HIGH);
208         
209         pinMode(triggerReset, OUTPUT);
210         // keep the reset low except when in trigger mode, so we don't accidentally trigger!
211         digitalWrite(triggerReset, LOW);
213         lcdInit();
214         encoderInit();
217 void incrementValue()
219         if(selected == 0)
220         {
221                 if(lapseRepresentation == 0)
222                         lapseTime++;
223                 else
224                         lapseTime += 60;
225         }
226         else
227         {
228                 if(exposureRepresentation == 0)
229                         exposureTime++;
230                 else
231                         exposureTime += 60;
232                 
233                 if(exposureTime > lapseTime)
234                         lapseTime = exposureTime;
235         }
238 void decrementValue()
240         if(selected == 0)
241         {
242                 if(lapseRepresentation == 0 || lapseTime == 60) // careful around transition; thanks, nate
243                         lapseTime--;
244                 else
245                         lapseTime -= 60;
246                 
247                 if(lapseTime < 0)
248                         lapseTime = 0;
249         }
250         else
251         {
252                 if(exposureRepresentation == 0 || exposureTime == 60)
253                         exposureTime--;
254                 else
255                         exposureTime -= 60;
256                 
257                 if(exposureTime < 0)
258                         exposureTime = 0;
259         }
262 void doEncoderA()
264         if(running)
265                 return;
266                 
267         noInterrupts();
268         delayMicroseconds(3000); // maximum bounce time, accd. to spec.
269                                                          // should we use optical encoders instead?
270         if (digitalRead(encoderPinA) == HIGH)
271         { 
272                 if (digitalRead(encoderPinB) == LOW)
273                         incrementValue();
274                 else
275                         decrementValue();
276         }
277         else                                       
278         {
279                 if (digitalRead(encoderPinB) == HIGH)
280                         incrementValue();
281                 else
282                         decrementValue();
283         }
284         
285         if(lapseTime >= 60)
286                 lapseRepresentation = 1;
287         else
288                 lapseRepresentation = 0;
290         if(exposureTime >= 60)
291                 exposureRepresentation = 1;
292         else
293                 exposureRepresentation = 0;
294         
295         updateEncoder = 1;
296         interrupts();
299 void doEncoderB()
301         if(running)
302                 return;
303         
304         noInterrupts();
305         delayMicroseconds(3000);
306         if (digitalRead(encoderPinB) == HIGH)
307         {
308                 if (digitalRead(encoderPinA) == HIGH)
309                         incrementValue();
310                 else
311                         decrementValue();
312         }
313         else
314         {
315                 if (digitalRead(encoderPinA) == LOW)
316                         incrementValue();
317                 else
318                         decrementValue();
319         }
320         
321         if(lapseTime >= 60)
322                 lapseRepresentation = 1;
323         else
324                 lapseRepresentation = 0;
326         if(exposureTime >= 60)
327                 exposureRepresentation = 1;
328         else
329                 exposureRepresentation = 0;
330         
331         updateEncoder = 1;
332         interrupts();
335 void switchModes()
337         unsigned long diff = (millis() - lastModeUpdate);
338         
339         if(diff < 300) // careful about the overflow...
340         {
341                 lastModeUpdate = millis();
342                 return;
343         }
344         else
345                 lastModeUpdate = millis();
346         
347         currentMode++;
348         if(currentMode > 3)
349                 currentMode = 0;
350         
351         if(currentMode == BULB)
352                 selected = 1;
353         else
354                 selected = 0;
355         
356         if(currentMode == TRIGGER)
357                 digitalWrite(triggerReset, HIGH);
358         else
359                 digitalWrite(triggerReset, LOW);
360                 
361         
362         updateHeader = 1;
365 void switchSelected()
367         if(currentMode == INTERVALBULB)
368         {
369                 unsigned long diff = (millis() - lastSelectedUpdate);
370                 
371                 if(diff < 300) // careful about the overflow...
372                 {
373                         lastSelectedUpdate = millis();
374                         return;
375                 }
376                 else
377                         lastSelectedUpdate = millis();
379                 selected = !selected;
380         }
381         
382         updateEncoder = 1;
385 void toggleRunning()
387         unsigned long diff = (millis() - lastToggleRunning);
388         
389         if(diff < 300) // careful about the overflow...
390         {
391                 lastToggleRunning = millis();
392                 return;
393         }
394         else
395                 lastToggleRunning = millis();
396         
397         running = !running;
398         updateEncoder = 1;
401 void drawTimecode(int secs, int rep)
403         if(rep == 0)
404         {
405                 lcdNumberWrite(secs);
406                 lcdDataWrite('"');
407         }
408         else
409         {
410                 lcdNumberWrite(secs/60);
411                 lcdDataWrite('\'');
412         }
415 int timecodeLength(int secs, int rep)
417         int count = 2;
418         
419         if(rep)
420                 secs = secs/60;
421         
422         int n1 = secs/100;
423         int n2 = (secs - n1 * 100) / 10;
425         if(n2)
426                 count++;
427         
428         return count;
431 char *modeHeader[4] = {"Interval", "            Bulb", "Interval    Bulb", "Trigger"};
433 void loop(void)
435         if(updateHeader)
436         {
437                 lcdHome(0);
438                 
439                 int count;
440                 for (count = 0; modeHeader[currentMode][count] != 0; count++)
441                         lcdDataWrite(modeHeader[currentMode][count]);
442                 for (; count < 16; count++)
443                         lcdDataWrite(' ');
444                 
445                 updateHeader = 0;
446                 updateEncoder = 1;
447         }
448                 
449         if(updateEncoder)
450         {       
451                 lcdHome(1);
452                 
453                 if(currentMode != BULB)
454                         drawTimecode(lapseTime, lapseRepresentation);
455                 
456                 int width = 0;
457                 
458                 if(currentMode != BULB)
459                         width += timecodeLength(lapseTime, lapseRepresentation);
460                 if(currentMode != INTERVAL && currentMode != TRIGGER)
461                         width += timecodeLength(exposureTime, exposureRepresentation);
462                         
463                 if(selected == 0)
464                 {
465                         lcdDataWrite(' '); width++;
466                         lcdDataWrite(127); width++;
467                 }
468                 else
469                 {
470                         width += 2;
471                 }
472                                 
473                 for(int i = 16; i > width; i--)
474                         lcdDataWrite(' ');
475                 
476                 if(selected == 1)
477                 {
478                         lcdDataWrite(126);
479                         lcdDataWrite(' ');
480                 }
481                 
482                 if(currentMode != INTERVAL && currentMode != TRIGGER)
483                         drawTimecode(exposureTime, exposureRepresentation);
485                 updateEncoder = 0;
486         }
487         
488         if(analogRead(buttonA) == 0)
489                 toggleRunning();
490         
491         if(running)
492         {
493                 lcdCommandWrite(0xD3); // /should/ shift us to middle of bottom row
494                 lcdDataWrite('*'); // animated?
495                 
496                 if(currentMode == TRIGGER)
497                 {
498                         // do special trigger stuff
499                         
500                         if(analogRead(triggerInput) < 100) // 100 might change with different resistors, make sure it works!
501                         {
502                                 digitalWrite(triggerReset, HIGH);
503                                 delay(100); // this should probably be at least the time of the delay...
504                                 digitalWrite(triggerReset, LOW);
505                                 delay(10);
506                                 digitalWrite(triggerReset, HIGH);
507                         }
508                         
509                         return;
510                 }
511                 
512                 unsigned long diff = millis() - lastShutter;
513                 
514                 int adjustedLapseTime = lapseTime;
515                 if(currentMode != INTERVAL)
516                         adjustedLapseTime -= exposureTime;
517                 
518                 if(diff > (adjustedLapseTime * 1000)) // careful about the overflow...
519                 {
520                         digitalWrite(cameraShutter, LOW);
521                         
522                         if(currentMode == INTERVAL)
523                                 delay(100);
524                         else
525                                 delay(exposureTime * 1000); //delay for length of exposure
526                                 
527                         digitalWrite(cameraShutter, HIGH);
528                         lastShutter = millis();
529                         
530                         if(currentMode == BULB)
531                                 running = 0;
532                 }
533         }
534         else
535         {
536                 if(analogRead(buttonB) == 0)
537                         switchModes();
538                 
539                 if(analogRead(encoderButton) == 0)
540                         switchSelected();
541         }