Introduction: Valentines Box
We share Valentine's at my office for team building. Everyone decorates a bag or box to collect the Valentines. Mine was a bit over the top. Shows blue eyes at baseline. Uses IR LED and sensor (http://www.radioshack.com/product/index.jsp?productId=2049723) to determine when a card is put through the slot. When that occurs the motor comes on and the card is pulled into the slot by the wheels.
Step 1: Red Eyes
Eyes turn red when card is inserted into slot and motor turns on. The LCD display increments the count of valentines received.
The bag is the one given to each employee to decorate. I used an Android robot toy from Google. The head comes off. I drilled out the eyes and inserted tri-color LEDs from Radio Shack (http://www.radioshack.com/product/index.jsp?productId=3020765).
There is a ribbon cable with going through a hole in the bag and a hole in the lid to connect to the prototype board connected to the Arduino (see next step). The cable as made using a ribbon cable (https://www.adafruit.com/products/793), and then the LEDs soldered in after stripping one end of the ribbon cables and attaching bare female connectors (http://www.pololu.com/catalog/product/1930), with some shrink tubing used to keep the wires from shorting.
Code is below:
/*************************************************************
Motor shield
https://www.instructables.com/id/Arduino-Motor-Shield-Tutorial/
Function pins per Ch. A pins per Ch. B
Direction D12 D13
PWM D3 D11
Brake D9 D8
Current Sensing A0 A1
lcd code from http://www.arduino.cc/playground/Learning/SparkFunSerLCD
LCD is 20x4
for Leonardo need to use Serial1 class
*************************************************************/
const int IRthreshold = 180;
const int IRPin = 3; // Read IR value on analog 3
const int SignalPin = A2; // send signal to sound Arduino
const int RedPin = A4;
const int BluePin = A5;
boolean cardsensed = false;
boolean LEDflag = false;
// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long timestamp = 0; // used to keep track of how long IR sensor blocked
const long MotorDelay = 500; // keep motor going for half a second after IR sensor clears
const long LEDdelay = 4000;
int val = 0;
int count = 0;
void setup()
{
//Setup Channel A
pinMode(12, OUTPUT); //Initiates Motor Channel A pin
pinMode(9, OUTPUT); //Initiates Brake Channel A pin
pinMode(SignalPin, OUTPUT);
digitalWrite(SignalPin, LOW);
pinMode(RedPin, OUTPUT);
pinMode(BluePin, OUTPUT);
Serial1.begin(9600);
delay(1000); // time to settle in, just in case
clearLCD();
backlightOn();
selectLineOne();
delay(10);
Serial1.print("Happy Valentines Day");
delay(10);
selectLineTwo();
Serial1.print(" CCE!");
delay(10);
selectLineThree();
Serial1.print("I now have:");
delay(10);
blueLED(); // blue at baseline
}
void loop()
{
val = analogRead(IRPin);
if (val > IRthreshold) // path blocked
{
cardsensed = true;
LEDflag = true;
timestamp = millis();
//forward @ full speed
digitalWrite(12, HIGH); //Establishes forward direction of Channel A
digitalWrite(9, LOW); //Disengage the Brake for Channel A
analogWrite(3, 255); //Spins the motor on Channel A at full speed
redLED(); // red when it's gotten a card
}
if(val < IRthreshold)
{
if(cardsensed)
{
if ((millis()-timestamp) >= MotorDelay)
{
cardsensed = false;
analogWrite(3,0); // turn off motor, but let it coast so no brake
count++;
selectLineFour();
Serial1.print(count);
Serial1.print(" Valentines");
digitalWrite(SignalPin, HIGH);
delay(20);
digitalWrite(SignalPin, LOW);
}
}
if(LEDflag)
{
if ((millis()-timestamp) >= LEDdelay)
{
LEDflag = false;
blueLED(); // back to being sad and blue
}
}
}
}
///////////////////////////// FUNCTIONS ////////////////////////////////
void selectLineOne(){ //puts the cursor at line 0 char 0.
Serial1.write(0xFE); //command flag
Serial1.write(128); //position
}
void selectLineTwo(){ //puts the cursor at line 2 char 0.
Serial1.write(0xFE); //command flag
Serial1.write(192); //position
}
void selectLineThree(){ //puts the cursor at line 3 char 0.
Serial1.write(0xFE); //command flag
Serial1.write(148); //position
}
void selectLineFour(){ //puts the cursor at line 4 char 0.
Serial1.write(0xFE); //command flag
Serial1.write(212); //position
}
void goTo(int position) { //position = line 1: 0-19, line 2: 20-39, etc, 79+ defaults back to 0
if (position<20){ Serial1.write(0xFE); //command flag
Serial1.write((position+128)); //position
}else if (position<40){Serial1.write(0xFE); //command flag
Serial1.write((position+128+64-20)); //position
}else if (position<60){Serial1.write(0xFE); //command flag
Serial1.write((position+128+20-40)); //position
}else if (position<80){Serial1.write(0xFE); //command flag
Serial1.write((position+128+84-60)); //position
} else { goTo(0); }
}
void clearLCD(){
Serial1.write(0xFE); //command flag
Serial1.write(0x01); //clear command.
}
void backlightOn(){ //turns on the backlight
Serial1.write(0x7C); //command flag for backlight stuff
Serial1.write(157); //light level.
}
void backlightOff(){ //turns off the backlight
Serial1.write(0x7C); //command flag for backlight stuff
Serial1.write(128); //light level for off.
}
void backlight50(){ //sets the backlight at 50% brightness
Serial1.write(0x7C); //command flag for backlight stuff
Serial1.write(143); //light level for off.
}
void serCommand(){ //a general function to call the command flag for issuing all other commands
Serial1.write(0xFE);
}
void blueLED(){
digitalWrite(BluePin, LOW); // reverse of usual since common
digitalWrite(RedPin, HIGH); // anode
}
void redLED(){
digitalWrite(BluePin, HIGH);
digitalWrite(RedPin, LOW);
}
The bag is the one given to each employee to decorate. I used an Android robot toy from Google. The head comes off. I drilled out the eyes and inserted tri-color LEDs from Radio Shack (http://www.radioshack.com/product/index.jsp?productId=3020765).
There is a ribbon cable with going through a hole in the bag and a hole in the lid to connect to the prototype board connected to the Arduino (see next step). The cable as made using a ribbon cable (https://www.adafruit.com/products/793), and then the LEDs soldered in after stripping one end of the ribbon cables and attaching bare female connectors (http://www.pololu.com/catalog/product/1930), with some shrink tubing used to keep the wires from shorting.
Code is below:
/*************************************************************
Motor shield
https://www.instructables.com/id/Arduino-Motor-Shield-Tutorial/
Function pins per Ch. A pins per Ch. B
Direction D12 D13
PWM D3 D11
Brake D9 D8
Current Sensing A0 A1
lcd code from http://www.arduino.cc/playground/Learning/SparkFunSerLCD
LCD is 20x4
for Leonardo need to use Serial1 class
*************************************************************/
const int IRthreshold = 180;
const int IRPin = 3; // Read IR value on analog 3
const int SignalPin = A2; // send signal to sound Arduino
const int RedPin = A4;
const int BluePin = A5;
boolean cardsensed = false;
boolean LEDflag = false;
// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long timestamp = 0; // used to keep track of how long IR sensor blocked
const long MotorDelay = 500; // keep motor going for half a second after IR sensor clears
const long LEDdelay = 4000;
int val = 0;
int count = 0;
void setup()
{
//Setup Channel A
pinMode(12, OUTPUT); //Initiates Motor Channel A pin
pinMode(9, OUTPUT); //Initiates Brake Channel A pin
pinMode(SignalPin, OUTPUT);
digitalWrite(SignalPin, LOW);
pinMode(RedPin, OUTPUT);
pinMode(BluePin, OUTPUT);
Serial1.begin(9600);
delay(1000); // time to settle in, just in case
clearLCD();
backlightOn();
selectLineOne();
delay(10);
Serial1.print("Happy Valentines Day");
delay(10);
selectLineTwo();
Serial1.print(" CCE!");
delay(10);
selectLineThree();
Serial1.print("I now have:");
delay(10);
blueLED(); // blue at baseline
}
void loop()
{
val = analogRead(IRPin);
if (val > IRthreshold) // path blocked
{
cardsensed = true;
LEDflag = true;
timestamp = millis();
//forward @ full speed
digitalWrite(12, HIGH); //Establishes forward direction of Channel A
digitalWrite(9, LOW); //Disengage the Brake for Channel A
analogWrite(3, 255); //Spins the motor on Channel A at full speed
redLED(); // red when it's gotten a card
}
if(val < IRthreshold)
{
if(cardsensed)
{
if ((millis()-timestamp) >= MotorDelay)
{
cardsensed = false;
analogWrite(3,0); // turn off motor, but let it coast so no brake
count++;
selectLineFour();
Serial1.print(count);
Serial1.print(" Valentines");
digitalWrite(SignalPin, HIGH);
delay(20);
digitalWrite(SignalPin, LOW);
}
}
if(LEDflag)
{
if ((millis()-timestamp) >= LEDdelay)
{
LEDflag = false;
blueLED(); // back to being sad and blue
}
}
}
}
///////////////////////////// FUNCTIONS ////////////////////////////////
void selectLineOne(){ //puts the cursor at line 0 char 0.
Serial1.write(0xFE); //command flag
Serial1.write(128); //position
}
void selectLineTwo(){ //puts the cursor at line 2 char 0.
Serial1.write(0xFE); //command flag
Serial1.write(192); //position
}
void selectLineThree(){ //puts the cursor at line 3 char 0.
Serial1.write(0xFE); //command flag
Serial1.write(148); //position
}
void selectLineFour(){ //puts the cursor at line 4 char 0.
Serial1.write(0xFE); //command flag
Serial1.write(212); //position
}
void goTo(int position) { //position = line 1: 0-19, line 2: 20-39, etc, 79+ defaults back to 0
if (position<20){ Serial1.write(0xFE); //command flag
Serial1.write((position+128)); //position
}else if (position<40){Serial1.write(0xFE); //command flag
Serial1.write((position+128+64-20)); //position
}else if (position<60){Serial1.write(0xFE); //command flag
Serial1.write((position+128+20-40)); //position
}else if (position<80){Serial1.write(0xFE); //command flag
Serial1.write((position+128+84-60)); //position
} else { goTo(0); }
}
void clearLCD(){
Serial1.write(0xFE); //command flag
Serial1.write(0x01); //clear command.
}
void backlightOn(){ //turns on the backlight
Serial1.write(0x7C); //command flag for backlight stuff
Serial1.write(157); //light level.
}
void backlightOff(){ //turns off the backlight
Serial1.write(0x7C); //command flag for backlight stuff
Serial1.write(128); //light level for off.
}
void backlight50(){ //sets the backlight at 50% brightness
Serial1.write(0x7C); //command flag for backlight stuff
Serial1.write(143); //light level for off.
}
void serCommand(){ //a general function to call the command flag for issuing all other commands
Serial1.write(0xFE);
}
void blueLED(){
digitalWrite(BluePin, LOW); // reverse of usual since common
digitalWrite(RedPin, HIGH); // anode
}
void redLED(){
digitalWrite(BluePin, HIGH);
digitalWrite(RedPin, LOW);
}
Step 2: Electronics
Most of circuit (motor, LED, LCD display) is run off of an Arduino Leonardo with an Arduino motor shield. See previous step for code.
The box says "Thank you very much" - it uses an Arduino Duemilanove with a Sparkfun VoiceBox shield (https://www.sparkfun.com/products/10661O).
Uses a simple signal (raises a pin high on the primary Arduino - senses the signal on the ViceBox Arduino)
has 3 random beeps before and after thank you - sounds a bit like R2D2
code is below
/*================================================================
Valentines thank you
Voice Box part based on code written by Ryan Owens, SparkFun Electronics
*/
//Soft serial library used to send serial commands on pin 2 instead of regular serial pin.
#include <SoftwareSerial.h>
const int UnusedPin = 4; // unused analog pin used to get random input for seed
const int SignalPin = A0; // to get signal from main Arduino
int signal;
//Define the Pin Numbers for the Voice Box part
#define E0 5
#define E1 6
#define E2 7
#define E3 8
#define E4 9
#define E5 10
#define E6 11
#define E7 12
#define RDY 13
#define RES 3
#define SPK 4
#define txPin 2
//Create a SoftSerial Objet
SoftwareSerial speakjet = SoftwareSerial(0, txPin);
char speakjetsetup[] = {20, 96, 21, 114, 22, 88, 23, 5, 0}; // for whatever reason Voice Box only seems to
// work correctly if lines end in 0
//thank you very much
char thankyou[] = {169, 8, 130, 142, 196, 6, 8, 160, 6, 166, 150, 7, 128, 6, 140, 134, 15, 8, 182, 0};
char pause[] = {5, 0}; // 5 = 60ms pause; 0 to end line
////////////////////////// SET UP ////////////////////////////////////////////
void setup() {
pinMode(SignalPin, INPUT);
//Configure the pins for the SpeakJet module
pinMode(txPin, OUTPUT);
pinMode(SPK, INPUT);
//Set up a serial port to talk from Arduino to the SpeakJet module on pin 3.
speakjet.begin(9600);
//Configure the Ready pin as an input
pinMode(RDY, INPUT);
//Configure Reset line as an output
pinMode(RES, OUTPUT);
//Configure all of the Event pins as outputs from Arduino, and set them Low.
for(int i=E0; i<=E7; i++)
{
pinMode(i, OUTPUT);
digitalWrite(i, LOW);
}
//All I/O pins are configured. Reset the SpeakJet module
digitalWrite(RES, LOW);
delay(100);
digitalWrite(RES, HIGH);
//send setup commands to SpeakJet
speakjet.println(speakjetsetup);
randomSeed(analogRead(UnusedPin));
delay(5000); // allow things to settle down
}
////////////////////////// LOOP ////////////////////////////////////////////
void loop()
{
signal = digitalRead(SignalPin);
if (signal == HIGH)
{
// redLED();
randombeeps(3);
delay(1000);
speakjet.print(thankyou);
delay(800);
randombeeps(3);
// delay(3000);
// blueLED();
}
} // end of loop
///////////////////////////// FUNCTIONS ////////////////////////////////
void randombeeps(int beeps)
{
for (int k=0; k<beeps; k++) // generate number of beeps
{
int rndnum = random(220, 230); // maps to beep sounds on voicebox, 220-229
char rndchar = char(rndnum);
speakjet.print(rndchar);
speakjet.print(pause);
}
}
void blueLED(){
digitalWrite(BluePin, HIGH);
digitalWrite(RedPin, LOW);
}
void redLED(){
digitalWrite(BluePin, LOW);
digitalWrite(RedPin, HIGH);
}
The box says "Thank you very much" - it uses an Arduino Duemilanove with a Sparkfun VoiceBox shield (https://www.sparkfun.com/products/10661O).
Uses a simple signal (raises a pin high on the primary Arduino - senses the signal on the ViceBox Arduino)
has 3 random beeps before and after thank you - sounds a bit like R2D2
code is below
/*================================================================
Valentines thank you
Voice Box part based on code written by Ryan Owens, SparkFun Electronics
*/
//Soft serial library used to send serial commands on pin 2 instead of regular serial pin.
#include <SoftwareSerial.h>
const int UnusedPin = 4; // unused analog pin used to get random input for seed
const int SignalPin = A0; // to get signal from main Arduino
int signal;
//Define the Pin Numbers for the Voice Box part
#define E0 5
#define E1 6
#define E2 7
#define E3 8
#define E4 9
#define E5 10
#define E6 11
#define E7 12
#define RDY 13
#define RES 3
#define SPK 4
#define txPin 2
//Create a SoftSerial Objet
SoftwareSerial speakjet = SoftwareSerial(0, txPin);
char speakjetsetup[] = {20, 96, 21, 114, 22, 88, 23, 5, 0}; // for whatever reason Voice Box only seems to
// work correctly if lines end in 0
//thank you very much
char thankyou[] = {169, 8, 130, 142, 196, 6, 8, 160, 6, 166, 150, 7, 128, 6, 140, 134, 15, 8, 182, 0};
char pause[] = {5, 0}; // 5 = 60ms pause; 0 to end line
////////////////////////// SET UP ////////////////////////////////////////////
void setup() {
pinMode(SignalPin, INPUT);
//Configure the pins for the SpeakJet module
pinMode(txPin, OUTPUT);
pinMode(SPK, INPUT);
//Set up a serial port to talk from Arduino to the SpeakJet module on pin 3.
speakjet.begin(9600);
//Configure the Ready pin as an input
pinMode(RDY, INPUT);
//Configure Reset line as an output
pinMode(RES, OUTPUT);
//Configure all of the Event pins as outputs from Arduino, and set them Low.
for(int i=E0; i<=E7; i++)
{
pinMode(i, OUTPUT);
digitalWrite(i, LOW);
}
//All I/O pins are configured. Reset the SpeakJet module
digitalWrite(RES, LOW);
delay(100);
digitalWrite(RES, HIGH);
//send setup commands to SpeakJet
speakjet.println(speakjetsetup);
randomSeed(analogRead(UnusedPin));
delay(5000); // allow things to settle down
}
////////////////////////// LOOP ////////////////////////////////////////////
void loop()
{
signal = digitalRead(SignalPin);
if (signal == HIGH)
{
// redLED();
randombeeps(3);
delay(1000);
speakjet.print(thankyou);
delay(800);
randombeeps(3);
// delay(3000);
// blueLED();
}
} // end of loop
///////////////////////////// FUNCTIONS ////////////////////////////////
void randombeeps(int beeps)
{
for (int k=0; k<beeps; k++) // generate number of beeps
{
int rndnum = random(220, 230); // maps to beep sounds on voicebox, 220-229
char rndchar = char(rndnum);
speakjet.print(rndchar);
speakjet.print(pause);
}
}
void blueLED(){
digitalWrite(BluePin, HIGH);
digitalWrite(RedPin, LOW);
}
void redLED(){
digitalWrite(BluePin, LOW);
digitalWrite(RedPin, HIGH);
}
Step 3: Gearing
Tricky part was the gears. I used Vex parts that I had on hand. in order to get both top and bottom wheels to spin I had to angle the gears. Note that the large gears are on a beam that is angles to engage the gears on the wheel axles.
The Vex motor works fine with the Arduino motor shield.
The Vex motor works fine with the Arduino motor shield.
Step 4:
For power I used a 9V regulated power supply. Even though the Vex motor is nominally 7V it worked OK (it was only on for brief periods). I used an Adafruit jack (https://www.adafruit.com/products/368) and some wire to bring the power to the Vin and ground terminal blocks (center positive).
The whole thing is contained in a plastic storage box I got on sale at Office Depot.
The whole thing is contained in a plastic storage box I got on sale at Office Depot.