Introduction: Automatic Multi-Photo Taker (Photobooth Style)

This is a tutorial on how to program your DSLR camera to take photos photobooth-style. It's a simple Arduino setup that allows you to take continuous photos with 3-second delay intervals. This was built for a college electronics project. A motion sensor detects motion in front of the camera, lights up and shows an LCD that prompts photo-takers to take photos. There are 5 settings in this tutorial - jump shot, single photo, two photos, three photos, and four photos. An HDMI monitor was hooked up in the end to show a live-view screen. 


Step 1: Materials and Equipment

Many of these items aren't required. Mostly the Arduino parts are necessary, but you'll get to see when we use what as you continue reading. 

Electronics, Hardware, and Other Equipment:
-- Arduino Uno Microcontroller
-- Motion Sensor with PIR (Sparkfun)
-- LCD Keypad Shield
-- Custom Shield
-- Camera (I used a Canon T3i)
-- Remote Switch (I used a Canon Remote Switch RS60 E3. Note that this isn't wireless)
-- Resistors: 10K, 220, 220, 100
-- Arduino Wall Adapter Power Supply
-- Arduino USB Cable (2.0)
-- RGB Controllable LED (Common Anode) 
-- 1N4148 Diode
-- Ultra-miniature, Highly Sensitive SPDT Relay for Signal Circuits (G5V-1 Low Signal Relay)
-- Wires
-- HDMI Monitor
-- Mini HDMI to HDMI Cable
-- Small Circuit Board
-- Box (to contain your setup)
-- Screws and standoffs
-- Plastic Tie Locks
-- Circuit Board Tape

Tools:
-- Soldering Iron
-- Drill Press
-- Screw drivers (plus and minus)
-- Wire cutters
-- Wire strippers

Step 2: Setting Up the Custom Shield

The shield in the pictures had been customized in our class to suit our needs. It looks slightly complicated, but all you have to pay attention to are these connections:

RGB LED attaches at the left-hand bottom corner. Red hooks to 100 ohms and to pin 13. Greens hooks up to 220 ohms and to pin 11. Green hooks to 220 ohms and pin 10. The data sheet for the LED - RGB Clear Common Anode is provided here:
http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Components/LED/YSL-R596AR3G4B5C-C10.pdf

The 6 screws in the upper left-hand corner corresponds to Term-60, Term-50, Term-40, Term-30, Term-20, Term-10 (from left to right). I've only used Term-60 (which is ground and hooks up to pin 9 of the relay), Term-30 (hooks up to Output Signal of motion detector), and Term-20 (hooks up to pin 2 of the relay). We'll get to relays soon.

The little hole near the RGB LED on the shield (the actual photo) is conveniently connected to 5V of the power of the Arduino.

Look at the 12CA, the two left pins are 5V and ground respectively. These will be used.

Once you've got your shield ready, you can connect it to your Arduino.

Step 3: Adding Your LCD

After you've connected your custom shield and Arduino Uno, just connect the LCD shield on top of your custom shield as shown in the picture. 

Step 4: The Motion Sensor

The Sparkfun PIR Motion Sensor has three wires (red, white, and black). Colors vary among motion sensors, but this this is how the connections work here:

Red --> Vcc
White --> Ground
Black --> Output Signal

Vcc and Output Signal were connected with a 10K pullup resistor. To learn more about how this motion sensor works visit this site:
http://bildr.org/2011/06/pir_arduino/

I hooked up the motion detector wires onto a small circuit board, connected the red and black wires with a 10K resistor, and hooked up the red wire to the 5V in the I2CA, the white wire to the Ground in the 12CA of the custom shield, and the black wire to Term-30.

Step 5: Camera Remote

Take your wire cutters and snip off the actual remote part of your Canon Remote Switch. Be sure to leave a long enough wire to connect to your camera. You should find three sets of wires. The outside silver wire coating is usually the ground. The other two are Shutter and Focus. You should plug in your cable to your camera and connect each wire to ground individually to figure out which wire is shutter or focus. I stripped the wires such that both Ground and Shutter (white wire) were easily accessible. I'll be hooking them up on the small circuit board soon.

To learn more about how the camera remote circuit works, check out:
http://www.doc-diy.net/photo/eos_wired_remote/

We will connect the shutter and ground (which makes the camera take a photo) using a relay (this acts as a switch).

Step 6: Your Relay (Switch)

Your relay acts as a switch to connect or disconnect your remote's Ground and Shutter. 

Based on the diagram for the relay, pins 2 and 9 represent the coil (note that there is not coil polarity). Pins 1 and 10 connect the switch. In my setup, here are the connections: 

Relay Pin 5 --> hook to remote's Ground
Relay Pin 10 --> hook to remote's Shutter
Relay Pin 2 --> hook to shield's Term 20 (this connects to Arduino Digital 5)
Relay 9 --> hook to shield's Term 60 (this is Ground)

To prevent any unwanted flow of charges, I hooked up a 1N4148 diode between pin 2 and pin 9 on the Relay. 


After you have this hooked up, organize both the Relay setup and the Motion Sensor setup on your small circuit board in an organized and clean manner. Refer to the picture with my setup. 

Then, you're ready to solder! Take your soldering iron and solder the wires on the back of the circuit board to connect all the wires you need to connect. After you finish, cover the back side with circuit board tape to prevent any accidental connections. 

To learn more about this relay: 
http://www.omron.com/ecb/products/pry/111/g5v_1.html
Data sheet here:
http://www.omron.com/ecb/products/pdf/en-g5v_1.pdf

Step 7: Checking the Setup

Be sure that your connections are set up as they are in the schematic.

Step 8: The Arduino Code

Our class had a few separate Arduino codes to make things easier for certain commands.
Keep these on file in your LIBRARY:

-------------------------------

Wire.h
-------------------------------
/*
TwoWire.h - TWI/I2C library for Arduino & Wiring
Copyright (c) 2006 Nicholas Zambetti. All right reserved.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
*/

#ifndef TwoWire_h
#define TwoWire_h

#include
#include "Stream.h"

#define BUFFER_LENGTH 32

class TwoWire : public Stream
{
private:
static uint8_t rxBuffer[];
static uint8_t rxBufferIndex;
static uint8_t rxBufferLength;

static uint8_t txAddress;
static uint8_t txBuffer[];
static uint8_t txBufferIndex;
static uint8_t txBufferLength;

static uint8_t transmitting;
static void (*user_onRequest)(void);
static void (*user_onReceive)(int);
static void onRequestService(void);
static void onReceiveService(uint8_t*, int);
public:
TwoWire();
void begin();
void begin(uint8_t);
void begin(int);
void beginTransmission(uint8_t);
void beginTransmission(int);
uint8_t endTransmission(void);
uint8_t endTransmission(uint8_t);
uint8_t requestFrom(uint8_t, uint8_t);
uint8_t requestFrom(uint8_t, uint8_t, uint8_t);
uint8_t requestFrom(int, int);
uint8_t requestFrom(int, int, int);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *, size_t);
virtual int available(void);
virtual int read(void);
virtual int peek(void);
virtual void flush(void);
void onReceive( void (*)(int) );
void onRequest( void (*)(void) );

inline size_t write(unsigned long n) { return write((uint8_t)n); }
inline size_t write(long n) { return write((uint8_t)n); }
inline size_t write(unsigned int n) { return write((uint8_t)n); }
inline size_t write(int n) { return write((uint8_t)n); }
using Print::write;
};

extern TwoWire Wire;


-----------------------------
Adafruit_MCP2307.h
-----------------------------
/***************************************************
This is a library for the MCP23017 i2c port expander

These displays use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/

#include
#include
#include "Adafruit_MCP23017.h"

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

// minihelper
static inline void wiresend(uint8_t x) {
#if ARDUINO >= 100
Wire.write((uint8_t)x);
#else
Wire.send(x);
#endif
}

static inline uint8_t wirerecv(void) {
#if ARDUINO >= 100
return Wire.read();
#else
return Wire.receive();
#endif
}

////////////////////////////////////////////////////////////////////////////////

void Adafruit_MCP23017::begin(uint8_t addr) {
if (addr > 7) {
addr = 7;
}
i2caddr = addr;

Wire.begin();


// set defaults!
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_IODIRA);
wiresend(0xFF); // all inputs on port A
Wire.endTransmission();

Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_IODIRB);
wiresend(0xFF); // all inputs on port B
Wire.endTransmission();
}


void Adafruit_MCP23017::begin(void) {
begin(0);
}

void Adafruit_MCP23017::pinMode(uint8_t p, uint8_t d) {
uint8_t iodir;
uint8_t iodiraddr;

// only 16 bits!
if (p > 15)
return;

if (p < 8)
iodiraddr = MCP23017_IODIRA;
else {
iodiraddr = MCP23017_IODIRB;
p -= 8;
}

// read the current IODIR
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(iodiraddr);
Wire.endTransmission();

Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
iodir = wirerecv();

// set the pin and direction
if (d == INPUT) {
iodir |= 1 << p;
} else {
iodir &= ~(1 << p);
}

// write the new IODIR
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(iodiraddr);
wiresend(iodir);
Wire.endTransmission();
}

uint16_t Adafruit_MCP23017::readGPIOAB() {
uint16_t ba = 0;
uint8_t a;

// read the current GPIO output latches
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_GPIOA);
Wire.endTransmission();

Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 2);
a = wirerecv();
ba = wirerecv();
ba <<= 8;
ba |= a;

return ba;
}

void Adafruit_MCP23017::writeGPIOAB(uint16_t ba) {
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_GPIOA);
wiresend(ba & 0xFF);
wiresend(ba >> 8);
Wire.endTransmission();
}

void Adafruit_MCP23017::digitalWrite(uint8_t p, uint8_t d) {
uint8_t gpio;
uint8_t gpioaddr, olataddr;

// only 16 bits!
if (p > 15)
return;

if (p < 8) {
olataddr = MCP23017_OLATA;
gpioaddr = MCP23017_GPIOA;
} else {
olataddr = MCP23017_OLATB;
gpioaddr = MCP23017_GPIOB;
p -= 8;
}

// read the current GPIO output latches
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(olataddr);
Wire.endTransmission();

Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
gpio = wirerecv();

// set the pin and direction
if (d == HIGH) {
gpio |= 1 << p;
} else {
gpio &= ~(1 << p);
}

// write the new GPIO
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(gpioaddr);
wiresend(gpio);
Wire.endTransmission();
}

void Adafruit_MCP23017::pullUp(uint8_t p, uint8_t d) {
uint8_t gppu;
uint8_t gppuaddr;

// only 16 bits!
if (p > 15)
return;

if (p < 8)
gppuaddr = MCP23017_GPPUA;
else {
gppuaddr = MCP23017_GPPUB;
p -= 8;
}


// read the current pullup resistor set
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(gppuaddr);
Wire.endTransmission();

Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
gppu = wirerecv();

// set the pin and direction
if (d == HIGH) {
gppu |= 1 << p;
} else {
gppu &= ~(1 << p);
}

// write the new GPIO
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(gppuaddr);
wiresend(gppu);
Wire.endTransmission();
}

uint8_t Adafruit_MCP23017::digitalRead(uint8_t p) {
uint8_t gpioaddr;

// only 16 bits!
if (p > 15)
return 0;

if (p < 8)
gpioaddr = MCP23017_GPIOA;
else {
gpioaddr = MCP23017_GPIOB;
p -= 8;
}

// read the current GPIO
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(gpioaddr);
Wire.endTransmission();

Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
return (wirerecv() >> p) & 0x1;
}



------------------------------------
Adafruit_RGBLCDShield.h
------------------------------------
/***************************************************
This is a library for the Adafruit RGB 16x2 LCD Shield
Pick one up at the Adafruit shop!
---------> http://http://www.adafruit.com/products/714

The shield uses I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/


#include "Adafruit_RGBLCDShield.h"

#include
#include
#include
#include

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set:
// DL = 1; 8-bit interface data
// N = 0; 1-line display
// F = 0; 5x8 dot character font
// 3. Display on/off control:
// D = 0; Display off
// C = 0; Cursor off
// B = 0; Blinking off
// 4. Entry mode set:
// I/D = 1; Increment by 1
// S = 0; No shift
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts (and the
// RGBLCDShield constructor is called).

Adafruit_RGBLCDShield::Adafruit_RGBLCDShield() {
_i2cAddr = 0;

_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;

// the I/O expander pinout
_rs_pin = 15;
_rw_pin = 14;
_enable_pin = 13;
_data_pins[0] = 12; // really d4
_data_pins[1] = 11; // really d5
_data_pins[2] = 10; // really d6
_data_pins[3] = 9; // really d7

_button_pins[0] = 0;
_button_pins[1] = 1;
_button_pins[2] = 2;
_button_pins[3] = 3;
_button_pins[4] = 4;
// we can't begin() yet :(
}




void Adafruit_RGBLCDShield::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
_rs_pin = rs;
_rw_pin = rw;
_enable_pin = enable;

_data_pins[0] = d0;
_data_pins[1] = d1;
_data_pins[2] = d2;
_data_pins[3] = d3;
_data_pins[4] = d4;
_data_pins[5] = d5;
_data_pins[6] = d6;
_data_pins[7] = d7;

_i2cAddr = 255;

_pinMode(_rs_pin, OUTPUT);
// we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
if (_rw_pin != 255) {
_pinMode(_rw_pin, OUTPUT);
}
_pinMode(_enable_pin, OUTPUT);


if (fourbitmode)
_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
else
_displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;

begin(16, 1);
}

void Adafruit_RGBLCDShield::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
// check if i2c
if (_i2cAddr != 255) {
//_i2c.begin(_i2cAddr);
Wire.begin();
_i2c.begin();

_i2c.pinMode(8, OUTPUT);
_i2c.pinMode(6, OUTPUT);
_i2c.pinMode(7, OUTPUT);
setBacklight(0x7);

if (_rw_pin)
_i2c.pinMode(_rw_pin, OUTPUT);

_i2c.pinMode(_rs_pin, OUTPUT);
_i2c.pinMode(_enable_pin, OUTPUT);
for (uint8_t i=0; i<4; i++)
_i2c.pinMode(_data_pins[i], OUTPUT);

for (uint8_t i=0; i<5; i++) {
_i2c.pinMode(_button_pins[i], INPUT);
_i2c.pullUp(_button_pins[i], 1);
}
}

if (lines > 1) {
_displayfunction |= LCD_2LINE;
}
_numlines = lines;
_currline = 0;

// for some 1 line displays you can select a 10 pixel high font
if ((dotsize != 0) && (lines == 1)) {
_displayfunction |= LCD_5x10DOTS;
}

// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
// according to datasheet, we need at least 40ms after power rises above 2.7V
// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
delayMicroseconds(50000);
// Now we pull both RS and R/W low to begin commands
_digitalWrite(_rs_pin, LOW);
_digitalWrite(_enable_pin, LOW);
if (_rw_pin != 255) {
_digitalWrite(_rw_pin, LOW);
}

//put the LCD into 4 bit or 8 bit mode
if (! (_displayfunction & LCD_8BITMODE)) {
// this is according to the hitachi HD44780 datasheet
// figure 24, pg 46

// we start in 8bit mode, try to set 4 bit mode
write4bits(0x03);
delayMicroseconds(4500); // wait min 4.1ms

// second try
write4bits(0x03);
delayMicroseconds(4500); // wait min 4.1ms

// third go!
write4bits(0x03);
delayMicroseconds(150);

// finally, set to 8-bit interface
write4bits(0x02);
} else {
// this is according to the hitachi HD44780 datasheet
// page 45 figure 23

// Send function set command sequence
command(LCD_FUNCTIONSET | _displayfunction);
delayMicroseconds(4500); // wait more than 4.1ms

// second try
command(LCD_FUNCTIONSET | _displayfunction);
delayMicroseconds(150);

// third go
command(LCD_FUNCTIONSET | _displayfunction);
}

// finally, set # lines, font size, etc.
command(LCD_FUNCTIONSET | _displayfunction);

// turn the display on with no cursor or blinking default
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
display();

// clear it off
clear();

// Initialize to default text direction (for romance languages)
_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
// set the entry mode
command(LCD_ENTRYMODESET | _displaymode);

}

/********** high level commands, for the user! */
void Adafruit_RGBLCDShield::clear()
{
command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
delayMicroseconds(2000); // this command takes a long time!
}

void Adafruit_RGBLCDShield::home()
{
command(LCD_RETURNHOME); // set cursor position to zero
delayMicroseconds(2000); // this command takes a long time!
}

void Adafruit_RGBLCDShield::setCursor(uint8_t col, uint8_t row)
{
int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
if ( row > _numlines ) {
row = _numlines-1; // we count rows starting w/0
}

command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

// Turn the display on/off (quickly)
void Adafruit_RGBLCDShield::noDisplay() {
_displaycontrol &= ~LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void Adafruit_RGBLCDShield::display() {
_displaycontrol |= LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turns the underline cursor on/off
void Adafruit_RGBLCDShield::noCursor() {
_displaycontrol &= ~LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void Adafruit_RGBLCDShield::cursor() {
_displaycontrol |= LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turn on and off the blinking cursor
void Adafruit_RGBLCDShield::noBlink() {
_displaycontrol &= ~LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void Adafruit_RGBLCDShield::blink() {
_displaycontrol |= LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// These commands scroll the display without changing the RAM
void Adafruit_RGBLCDShield::scrollDisplayLeft(void) {
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void Adafruit_RGBLCDShield::scrollDisplayRight(void) {
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// This is for text that flows Left to Right
void Adafruit_RGBLCDShield::leftToRight(void) {
_displaymode |= LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void Adafruit_RGBLCDShield::rightToLeft(void) {
_displaymode &= ~LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'right justify' text from the cursor
void Adafruit_RGBLCDShield::autoscroll(void) {
_displaymode |= LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'left justify' text from the cursor
void Adafruit_RGBLCDShield::noAutoscroll(void) {
_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void Adafruit_RGBLCDShield::createChar(uint8_t location, uint8_t charmap[]) {
location &= 0x7; // we only have 8 locations 0-7
command(LCD_SETCGRAMADDR | (location << 3));
for (int i=0; i<8; i++) {
write(charmap[i]);
}
command(LCD_SETDDRAMADDR); // unfortunately resets the location to 0,0
}

/*********** mid level commands, for sending data/cmds */

inline void Adafruit_RGBLCDShield::command(uint8_t value) {
send(value, LOW);
}

#if ARDUINO >= 100
inline size_t Adafruit_RGBLCDShield::write(uint8_t value) {
send(value, HIGH);
return 1;
}
#else
inline void Adafruit_RGBLCDShield::write(uint8_t value) {
send(value, HIGH);
}
#endif

/************ low level data pushing commands **********/

// little wrapper for i/o writes
void Adafruit_RGBLCDShield::_digitalWrite(uint8_t p, uint8_t d) {
if (_i2cAddr != 255) {
// an i2c command
_i2c.digitalWrite(p, d);
} else {
// straightup IO
digitalWrite(p, d);
}
}

// Allows to set the backlight, if the LCD backpack is used
void Adafruit_RGBLCDShield::setBacklight(uint8_t status) {
// check if i2c or SPI
_i2c.digitalWrite(8, ~(status >> 2) & 0x1);
_i2c.digitalWrite(7, ~(status >> 1) & 0x1);
_i2c.digitalWrite(6, ~status & 0x1);
}

// little wrapper for i/o directions
void Adafruit_RGBLCDShield::_pinMode(uint8_t p, uint8_t d) {
if (_i2cAddr != 255) {
// an i2c command
_i2c.pinMode(p, d);
} else {
// straightup IO
pinMode(p, d);
}
}

// write either command or data, with automatic 4/8-bit selection
void Adafruit_RGBLCDShield::send(uint8_t value, uint8_t mode) {
_digitalWrite(_rs_pin, mode);

// if there is a RW pin indicated, set it low to Write
if (_rw_pin != 255) {
_digitalWrite(_rw_pin, LOW);
}

if (_displayfunction & LCD_8BITMODE) {
write8bits(value);
} else {
write4bits(value>>4);
write4bits(value);
}
}

void Adafruit_RGBLCDShield::pulseEnable(void) {
_digitalWrite(_enable_pin, LOW);
delayMicroseconds(1);
_digitalWrite(_enable_pin, HIGH);
delayMicroseconds(1); // enable pulse must be >450ns
_digitalWrite(_enable_pin, LOW);
delayMicroseconds(100); // commands need > 37us to settle
}

void Adafruit_RGBLCDShield::write4bits(uint8_t value) {
if (_i2cAddr != 255) {
uint16_t out = 0;

out = _i2c.readGPIOAB();

// speed up for i2c since its sluggish
for (int i = 0; i < 4; i++) {
out &= ~_BV(_data_pins[i]);
out |= ((value >> i) & 0x1) << _data_pins[i];
}

// make sure enable is low
out &= ~ _BV(_enable_pin);

_i2c.writeGPIOAB(out);

// pulse enable
delayMicroseconds(1);
out |= _BV(_enable_pin);
_i2c.writeGPIOAB(out);
delayMicroseconds(1);
out &= ~_BV(_enable_pin);
_i2c.writeGPIOAB(out);
delayMicroseconds(100);

} else {
for (int i = 0; i < 4; i++) {
_pinMode(_data_pins[i], OUTPUT);
_digitalWrite(_data_pins[i], (value >> i) & 0x01);
}
pulseEnable();
}
}

void Adafruit_RGBLCDShield::write8bits(uint8_t value) {
for (int i = 0; i < 8; i++) {
_pinMode(_data_pins[i], OUTPUT);
_digitalWrite(_data_pins[i], (value >> i) & 0x01);
}

pulseEnable();
}

uint8_t Adafruit_RGBLCDShield::readButtons(void) {
uint8_t reply = 0x1F;

for (uint8_t i=0; i<5; i++) {
reply &= ~((_i2c.digitalRead(_button_pins[i])) << i);
}
return reply;
}


----------------------------------------
Photobooth Code (this is the one you'll actually be using)
----------------------------------------

#include
#include
#include

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// Set backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7

int shutterPin = 5; //digital 5 that connects to camera shutter
int pirPin = 6; //digital 6
int intMotion = 0;
int intNoMotion = 0;

int greenLED=11;
int blueLED=10;
int redLED=13;

boolean buttonselect;

void setup(){
Serial.begin(9600);
pinMode(pirPin, INPUT);
lcd.begin(16, 2);
int time = millis();
time = millis() - time;

pinMode(greenLED, OUTPUT);
pinMode(blueLED, OUTPUT);
pinMode(redLED, OUTPUT);
pinMode(shutterPin, OUTPUT);
}


void loop(){
uint8_t buttons = lcd.readButtons();
int pirVal = digitalRead(pirPin);

if(pirVal == LOW){ //was motion detected
Serial.println("Motion Detected");
delay(1000);
intMotion = intMotion+1;
if(intMotion>3){
lcd.setBacklight(VIOLET);
lcd.setCursor(0, 0);
lcd.print (" CHLOE'S CAMERA ");
lcd.setCursor (0,1);
lcd.print ("Take a photo!!!!");
delay(2000);
lcd.setCursor(0,0);
lcd.print("SELECT for PIC!");
lcd.setCursor(0,1);
lcd.print("UP for JUMP!");
delay(3000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("LEFT: 2 RIGHT: 4");
lcd.setCursor(0,1);
lcd.print("DOWN: 3 PICS :)");
delay(1000);
}
}

if (buttons) {
lcd.clear();
lcd.setCursor(0,0);
//SELECT BUTTON --> TAKES 1 PHOTO
if (buttons & BUTTON_SELECT) {
intMotion = 0;

lcd.clear();
lcd.print("Taking a photo!!");
lcd.setBacklight(YELLOW);
delay(1000);
lcd.clear();

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
}

//UP BUTTON --> TAKES JUMP SHOTS -- 3 CONSECUTIVE SHOTS
if (buttons & BUTTON_UP) {
intMotion = 0;
RGBjump();
}

//LEFT BUTTON --> 2 photos
if (buttons & BUTTON_LEFT) {
intMotion = 0;

lcd.clear();
lcd.print("Taking 2 photos!!");
lcd.setBacklight(YELLOW);
delay(1000);
lcd.clear();

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
delay(100);
lcd.clear();

RGBtest();
digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
}

//DOWN BUTTON --> 3 photos
if (buttons & BUTTON_DOWN) {
intMotion = 0;

lcd.clear();
lcd.print("Taking 3 photos!!");
lcd.setBacklight(YELLOW);
delay(1000);
lcd.clear();

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
delay(100);

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
delay(100);

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
delay(100);
}

//BUTTON RIGHT --> TAKES 4 PHOTOS
if (buttons & BUTTON_RIGHT) {
intMotion = 0;

lcd.clear();
lcd.print("Taking 4 photos!!");
lcd.setBacklight(YELLOW);
delay(1000);
lcd.clear();

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
delay(100);

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
delay(100);

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
delay(100);

RGBtest();

digitalWrite(shutterPin, HIGH);
delay(500);
digitalWrite(shutterPin, LOW);
delay(100);
}
}

else if (pirVal == HIGH) {
Serial.println("No Motion Detected");
delay(1000);
intMotion = 0;
intNoMotion = intNoMotion+1;
if(intNoMotion>3){
lcd.setBacklight(WHITE);
lcd.clear();}
}
}




void RGBtest()
{ //Low is on the color that lights bc we're dealing with a common anode--so things are backwards
//RED light
lcd.clear();
digitalWrite(redLED, LOW);
digitalWrite(blueLED, HIGH);
digitalWrite(greenLED, HIGH);
lcd.setBacklight(RED);
lcd.setCursor (3,0);
lcd.print ("3...EEKKK!!");
delay(1000);
lcd.clear();

//BLUE light
digitalWrite(redLED, HIGH);
digitalWrite(blueLED, LOW);
digitalWrite(greenLED, HIGH);
lcd.setBacklight(GREEN);
lcd.setCursor (3,0);
lcd.print ("2...OMG!!!");
delay(1000);
lcd.clear();

//GREEN light
digitalWrite(redLED, HIGH);
digitalWrite(blueLED, HIGH);
digitalWrite(greenLED, LOW);
lcd.setBacklight(BLUE);
lcd.setCursor (3,0);
lcd.print ("1...Ready??");
delay(1000);
lcd.clear();

//SMILE!
digitalWrite(redLED, HIGH);
digitalWrite(blueLED, HIGH);
digitalWrite(greenLED, HIGH);
lcd.clear();
lcd.setBacklight(VIOLET);
lcd.print("SMILE!");
return; }


void RGBjump()
{
//Low is on the color that lights bc we're dealing with a common anode (so things are backwards)
lcd.clear();
lcd.print("Jump after one!!!");
lcd.setBacklight(YELLOW);
delay(1000);
lcd.clear();

//RED light
digitalWrite(redLED, LOW);
digitalWrite(blueLED, HIGH);
digitalWrite(greenLED, HIGH);
lcd.setBacklight(RED);
lcd.setCursor (3,0);
lcd.print ("3...Get ready!!");
delay(1000);
lcd.clear();

//BLUE light
digitalWrite(redLED, HIGH);
digitalWrite(blueLED, LOW);
digitalWrite(greenLED, HIGH);
lcd.setBacklight(GREEN);
lcd.setCursor (3,0);
lcd.print ("2...Get set!!!");
delay(1000);
lcd.clear();

//GREEN light
digitalWrite(redLED, HIGH);
digitalWrite(blueLED, HIGH);
digitalWrite(greenLED, LOW);
lcd.setBacklight(BLUE);
lcd.setCursor (3,0);
lcd.print ("1...And.....");
delay(1000);
lcd.clear();

//Smile!
digitalWrite(redLED, HIGH);
digitalWrite(blueLED, HIGH);
digitalWrite(greenLED, HIGH);
lcd.clear();
lcd.setBacklight(VIOLET);
lcd.print("JUMP!!!!");

digitalWrite(shutterPin, HIGH);
delay(300);
digitalWrite(shutterPin, LOW);
delay(25);

digitalWrite(shutterPin, HIGH);
delay(300);
digitalWrite(shutterPin, LOW);
delay(25);

digitalWrite(shutterPin, HIGH);
delay(400);
digitalWrite(shutterPin, LOW);
delay(50);

return;
}


Step 9: Checking Its Functions

The code sets up the motion detector to print on the serial monitor "Motion Detected". If motion has been detected for a few seconds, the screen pops up and says "CHLOE'S CAMERA" and it gives a summary of what buttons do what.

UP: Jump Shot
LEFT: 2 photos
DOWN: 3 photos
RIGHT: 4 photos
SELECT: 1 photo

What the jump shot does is that when you press it, the LED goes red, green, blue and the LCD goes "3...2...1..." and triggers the shutter 3 quick consecutive times. Since I was using a DSLR camera, I wasn't able to use live-view to do the jump shot because there was too much lag. Also, on a side note, if you're using the DSLR camera, be sure to autofocus on your subjects first and then leave the camera on manual focus. Sometimes the shutter+ground combo doesn't focus in time when the relay is connecting the two wires and no photo is taken. Try keeping a high ISO, fast shutter speed, and a small aperture (which is a large value). You don't want people to be blurred out in your photos.

The motion detected is programmed so that when there is "No Motion Detected" for a certain amount of time, the LCD goes blank. This is like how when a photobooth turns on when you walk into it and it turns off when you leave. People have told me to trigger the camera with motion, but I wanted to do it this way. You should definitely get creative and trigger the camera in other ways.

Step 10: Let's Put It Together...

Put everything together!!!
I put all of my electronics in a little black box as shown in the pictures. This step is really just however you wish to organize your things.
I used little plastic locks to wrap around my power supply cable and my remote cable in the inside of my box just so they wouldn't be unplugged from the inside.

Annnddd....YOU'RE DONE!! (: Enjoy taking fun photos with Elecyour friends and family!