Assign digital pots.
[arduino-intervalometer.git] / Intervalometer.pde
blob78cd9c37bc4b3d4430e5f501a2a68cdfac432adf
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 // Slow down encoder in second range/normally
17 // Hour representation?
18 // Either need to support more digits, or arbitrarily limit durations
19 // Fix when trigger is triggered and someone (ROBB) stops it (and it keeps open)
20 // Why does trigger reset not get put back when we're done triggering!?
21 // External power supply?
24  *  Pin Assignment
25  */
27 int cameraShutter = 0;  // Pulse low for shutter release
29 int triggerReset =  1;  // Resets trigger latch on low pulse
30                                                 // Prevents trigger if held low
32 int encoderPinA =       2;      // HW interrupt
33 int encoderPinB =       3;      // HW interrupt
35 int lcdPower =          4;      // Pull low to power the LCD backlight
36 int lcdEnable =         7;      // LCD Enable - pin 6 on LCD module
37 int lcdClock =          8;      // Shift register clock
38 int lcdData =           9;      // Shift register data
40 int potSelect =         10; // SPI (SS) for digital pots
41 int potData =           11; // SPI (MOSI) for digital pots
42 int badSPIpin =         12;     // SPI (MISO) for nothing, unusable
43 int potClock =          13; // SPI (SCK) for digital pots
45 int triggerInput =  2; // Feedback for trigger reset
46 int encoderButton =     17; // Encoder pushbutton
47 int buttonA =           18; // Left pushbutton
48 int buttonB =           19; // Right pushbutton
51  *  Digital Pot Assignment
52  */
54 int ledRPot = 3;
55 int ledGPot = 1;
56 int ledBPot = 0;
58 int timerPot = 4;
59 int contrastPot = 5;
61 /////////////////////////////
63 int writing = 0;
65 int currentMode = INTERVAL, selected = 0;
67 // Debouncing time variables
68 unsigned long lastToggleRunning = 0;
69 unsigned long lastModeUpdate = 0;
70 unsigned long lastSelectedUpdate = 0;
71 unsigned long lastShutter = 0;
73 // Update statuses
75 volatile int updateHeader = 1, updateEncoder = 1, running = 0;
77 // Exposure and Timelapse durations
78 volatile long exposureTime = 0, lapseTime = 0;
79 volatile int exposureRepresentation = 0, lapseRepresentation = 0;
82  *  Higher level hardware functions
83  */
85 void pulsePin(int pin, int value)
87         digitalWrite(pin, !value);
88         delay(1);
89         digitalWrite(pin, value);
90         delay(1);
91         digitalWrite(pin, !value);
92         delay(1);
95 void parallelShiftOut(int firstPin, int lastPin, int & value)
97         int i;
98         for(i = firstPin; i <= lastPin; i++)
99         {
100                 digitalWrite(i, value & 0x01);
101                 value >>= 1;
102         }
106  *  Digital Pot Control Functions
107  */
109 char spi_transfer(volatile char data)
111         SPDR = data;
112         while (!(SPSR & (1<<SPIF))) {};
113         return SPDR;
116 void digitalPotInit()
118         byte i;
119         byte clr;
120         pinMode(potData, OUTPUT);
121         pinMode(potClock,OUTPUT);
122         pinMode(potSelect,OUTPUT);
123         digitalWrite(potSelect,HIGH);
124         SPCR = (1<<SPE)|(1<<MSTR);
125         clr=SPSR;
126         clr=SPDR;
127         delay(10);
128         for (i=0;i<6;i++)
129                 write_pot(i,255);
132 byte write_pot(int address, int value)
134         digitalWrite(potSelect,LOW);
135         spi_transfer(address);
136         spi_transfer(value);
137         digitalWrite(potSelect,HIGH);
141  *  LCD Control Functions
142  */
144 void lcdDataWrite(byte a)
146         writing = 1;
147         shiftOut(lcdData, lcdClock, LSBFIRST, 0x20 + ((a >> 4) & 0xF));
148         digitalWrite(lcdEnable, HIGH);
149         delayMicroseconds(1);
150         digitalWrite(lcdEnable, LOW);
152         delay(1);
154         shiftOut(lcdData, lcdClock, LSBFIRST, 0x20 + (a & 0xF));
155         digitalWrite(lcdEnable, HIGH);
156         delayMicroseconds(1);
157         digitalWrite(lcdEnable, LOW);
158         
159         delay(1);
160         writing = 0;
163 void lcdCommandWrite(int a)
165         writing = 1;
166         shiftOut(lcdData, lcdClock, LSBFIRST, (a >> 4) & 0xF);
167         digitalWrite(lcdEnable, HIGH);
168         delayMicroseconds(1);
169         digitalWrite(lcdEnable, LOW);
171         delay(1);
173         shiftOut(lcdData, lcdClock, LSBFIRST, a & 0xF);
174         digitalWrite(lcdEnable, HIGH);
175         delayMicroseconds(1);
176         digitalWrite(lcdEnable, LOW);
177         
178         delay(1);
179         writing = 0;
182 void lcdNumberWrite(int nr)
184         int n1 = nr/100;
185         int n2 = (nr - n1 * 100) / 10;
186         
187         if(n2)
188                 lcdDataWrite('0' + n2);
189         
190         nr = nr - n1 * 100 - n2 * 10;
191         lcdDataWrite('0' + nr);
194 void lcdHome(int row)
196         lcdCommandWrite(0x02);
197         
198         delay(4);
199         
200         if(row == 1)
201                 lcdCommandWrite(0xC0);
202                 
203         delay(1);
206 void lcdClear()
208         lcdCommandWrite(0x01);
209         delay(4);
213  *  Hardware initialization functions
214  */
216 void lcdInit()
218         int i = 0;
219         
220         pinMode(lcdEnable, OUTPUT);
221         pinMode(lcdData, OUTPUT);
222         pinMode(lcdClock, OUTPUT);
223         
224         digitalWrite(lcdClock,LOW);
225         digitalWrite(lcdData,LOW);
226         digitalWrite(lcdEnable,LOW);
227         
228         // there's a chance that when we drop to not running 
229         // on top of the arduino bootloader, we'll need a somewhat
230         // significant delay here (called for in the spec, but the bl is slow)
232         lcdCommandWrite(0x03); delay(5);
233         lcdCommandWrite(0x03); delay(1);
234         lcdCommandWrite(0x03); delay(1);        
235         lcdCommandWrite(0x02); delay(4);
236         lcdCommandWrite(0x06); delay(1);
237         lcdCommandWrite(0x0C); delay(1);
238         lcdCommandWrite(0x01); delay(4);
239         lcdCommandWrite(0x80); delay(1);
240         
241         pinMode(lcdPower, OUTPUT);
242         digitalWrite(lcdPower, LOW);
245 void encoderInit()
247         pinMode(encoderPinA, INPUT); 
248         digitalWrite(encoderPinA, HIGH);
249         pinMode(encoderPinB, INPUT); 
250         digitalWrite(encoderPinB, HIGH);
252         attachInterrupt(0, doEncoderA, CHANGE);
253         attachInterrupt(1, doEncoderB, CHANGE);
256 void setup (void)
258         pinMode(cameraShutter, OUTPUT);
259         digitalWrite(cameraShutter, HIGH);
260         
261         pinMode(triggerReset, OUTPUT);
262         // keep the reset high except when in trigger mode, so we don't accidentally trigger!
263         digitalWrite(triggerReset, LOW);
265         digitalPotInit();
266         lcdInit();
267         encoderInit();
270 void incrementValue()
272         if(selected == 0)
273         {
274                 if(lapseRepresentation == 0)
275                         lapseTime++;
276                 else
277                         lapseTime += 60;
278         }
279         else
280         {
281                 if(exposureRepresentation == 0)
282                         exposureTime++;
283                 else
284                         exposureTime += 60;
285                 
286                 if(exposureTime > lapseTime)
287                         lapseTime = exposureTime;
288         }
291 void decrementValue()
293         if(selected == 0)
294         {
295                 if(lapseRepresentation == 0 || lapseTime == 60) // careful around transition; thanks, nate
296                         lapseTime--;
297                 else
298                         lapseTime -= 60;
299                 
300                 if(lapseTime < 0)
301                         lapseTime = 0;
302         }
303         else
304         {
305                 if(exposureRepresentation == 0 || exposureTime == 60)
306                         exposureTime--;
307                 else
308                         exposureTime -= 60;
309                 
310                 if(exposureTime < 0)
311                         exposureTime = 0;
312         }
315 void updateTimeRepresentations()
317         if(lapseTime >= 60)
318                 lapseRepresentation = 1;
319         else
320                 lapseRepresentation = 0;
322         if(exposureTime >= 60)
323                 exposureRepresentation = 1;
324         else
325                 exposureRepresentation = 0;
326         
327         updateEncoder = 1;
330 void doEncoderA()
332 //      if(running)
333 //              return;
334                 
335         noInterrupts();
336         delayMicroseconds(3000); // maximum bounce time, accd. to spec.
338         if (digitalRead(encoderPinA) == HIGH)
339         { 
340                 if (digitalRead(encoderPinB) == LOW)
341                         incrementValue();
342                 else
343                         decrementValue();
344         }
345         else                                       
346         {
347                 if (digitalRead(encoderPinB) == HIGH)
348                         incrementValue();
349                 else
350                         decrementValue();
351         }
352         
353         updateTimeRepresentations();
354         
355         updateEncoder = 1;
356         interrupts();
359 void doEncoderB()
361 //      if(running)
362 //              return;
363         
364         noInterrupts();
365         delayMicroseconds(3000);
366         if (digitalRead(encoderPinB) == HIGH)
367         {
368                 if (digitalRead(encoderPinA) == HIGH)
369                         incrementValue();
370                 else
371                         decrementValue();
372         }
373         else
374         {
375                 if (digitalRead(encoderPinA) == LOW)
376                         incrementValue();
377                 else
378                         decrementValue();
379         }
380         
381         updateTimeRepresentations();
382         
383         updateEncoder = 1;
384         interrupts();
387 void switchModes()
389         unsigned long diff = (millis() - lastModeUpdate);
390         
391         if(diff < 300) // careful about the overflow...
392         {
393                 lastModeUpdate = millis();
394                 return;
395         }
396         else
397                 lastModeUpdate = millis();
398         
399         currentMode++;
400         if(currentMode > 3)
401                 currentMode = 0;
402         
403         if(currentMode == BULB)
404                 selected = 1;
405         else
406                 selected = 0;
407         
408         if(currentMode == TRIGGER)
409                 digitalWrite(triggerReset, HIGH);
410         else
411                 digitalWrite(triggerReset, LOW);
412                 
413         
414         updateHeader = 1;
417 void switchSelected()
419         if(currentMode == INTERVALBULB)
420         {
421                 unsigned long diff = (millis() - lastSelectedUpdate);
422                 
423                 if(diff < 300) // careful about the overflow...
424                 {
425                         lastSelectedUpdate = millis();
426                         return;
427                 }
428                 else
429                         lastSelectedUpdate = millis();
431                 selected = !selected;
432         }
433         
434         updateEncoder = 1;
437 void toggleRunning()
439         unsigned long diff = (millis() - lastToggleRunning);
440         
441         if(diff < 300) // careful about the overflow...
442         {
443                 lastToggleRunning = millis();
444                 return;
445         }
446         else
447                 lastToggleRunning = millis();
448         
449         running = !running;
450         
451         updateEncoder = 1;
454 void drawTimecode(int secs, int rep)
456         if(rep == 0)
457         {
458                 lcdNumberWrite(secs);
459                 lcdDataWrite('"');
460         }
461         else
462         {
463                 lcdNumberWrite(secs/60);
464                 lcdDataWrite('\'');
465         }
468 int timecodeLength(int secs, int rep)
470         int count = 2;
471         
472         if(rep)
473                 secs = secs/60;
474         
475         int n1 = secs/100;
476         int n2 = (secs - n1 * 100) / 10;
478         if(n2)
479                 count++;
480         
481         return count;
484 char *modeHeader[4] = {"Interval", "            Bulb", "Interval    Bulb", "Trigger"};
486 int pot = 765;
487 int fadeUp = 1;
489 void loop(void)
491         if(fadeUp)
492         {
493                 write_pot(ledRPot, 20);
494                 write_pot(ledGPot, 255);
495                 write_pot(ledBPot, 255);
496                 write_pot(timerPot, 255);
497         }
498         
499         if(fadeUp)
500                 write_pot(contrastPot, --pot / 3.0);
501         if(pot == 50)
502                 fadeUp = 0;
503                 
504         if(updateHeader)
505         {
506                 lcdHome(0);
507                 
508                 int count;
509                 for (count = 0; modeHeader[currentMode][count] != 0; count++)
510                         lcdDataWrite(modeHeader[currentMode][count]);
511                 for (; count < 16; count++)
512                         lcdDataWrite(' ');
513                 
514                 updateHeader = 0;
515                 updateEncoder = 1;
516         }
517                 
518         if(updateEncoder)
519         {
520                 lcdHome(1);
521                 
522                 if(currentMode != BULB)
523                         drawTimecode(lapseTime, lapseRepresentation);
524                 
525                 int width = 0;
526                 
527                 if(currentMode != BULB)
528                         width += timecodeLength(lapseTime, lapseRepresentation);
529                 if(currentMode != INTERVAL && currentMode != TRIGGER)
530                         width += timecodeLength(exposureTime, exposureRepresentation);
531                         
532                 if(selected == 0)
533                 {
534                         lcdDataWrite(' '); width++;
535                         lcdDataWrite(127); width++;
536                 }
537                 else
538                 {
539                         width += 2;
540                 }
541                                 
542                 for(int i = 16; i > width; i--)
543                         lcdDataWrite(' ');
544                 
545                 if(selected == 1)
546                 {
547                         lcdDataWrite(126);
548                         lcdDataWrite(' ');
549                 }
550                 
551                 if(currentMode != INTERVAL && currentMode != TRIGGER)
552                         drawTimecode(exposureTime, exposureRepresentation);
553                 
554                 updateEncoder = 0;
555         }
556         
557         if(!digitalRead(buttonA))
558                 toggleRunning();
559         
560         if(running)
561         {
562                 digitalWrite(lcdPower, HIGH);
563                 
564                 if(currentMode == TRIGGER)
565                 {
566                         if(analogRead(triggerInput) < 100) // 100 might change with different resistors, make sure it works!
567                         {
568                                 digitalWrite(triggerReset, HIGH);
569                                 delay(100); // this should probably be at least the time of the delay from signal (in the 555)...
570                                 digitalWrite(triggerReset, LOW);
571                                 delay(10);
572                                 digitalWrite(triggerReset, HIGH);
573                         }
574                         
575                         return;
576                 }
577                 
578                 unsigned long diff = millis() - lastShutter;
579                 
580                 int adjustedLapseTime = lapseTime;
581                 if(currentMode != INTERVAL)
582                         adjustedLapseTime -= exposureTime;
583                 
584                 if(diff > (adjustedLapseTime * 1000)) // careful about the overflow...
585                 {
586                         digitalWrite(cameraShutter, LOW);
587                         
588                         if(currentMode == INTERVAL)
589                                 delay(100);
590                         else
591                                 delay(exposureTime * 1000); //delay for length of exposure
592                                 // biggest problem with this is that you can't stop a bulb (of either type)
593                                 // in the middle... you have to power off the intervalometer; same as you would have 
594                                 // to do with a camera, I suppose, so people might be used to it.
595                                 // however, we can get around this by looping and checking millis()...
596                                 
597                         digitalWrite(cameraShutter, HIGH);
598                         lastShutter = millis();
599                         
600                         if(currentMode == BULB)
601                                 running = 0;
602                 }
603         }
604         else
605         {
606                 digitalWrite(lcdPower, LOW);
607                 
608                 if(!digitalRead(buttonB))
609                         switchModes();
610                 
611                 if(!digitalRead(encoderButton))
612                         switchSelected();
613         }