Showing posts with label arduino. Show all posts
Showing posts with label arduino. Show all posts

Trashcan Accelerometer

 
The city that I live in provides weekly trash collection services. Customers typically have a 96 gallon trashcan which is left out by the curb early in the morning on a scheduled day. Sometime during the day a large truck arrives and the lone driver/operator activates a giant claw that reaches out, grabs the trash container, lifts it up into the air and empties the contents into the back collection area of the vehicle. Then the giant claw drops the container back down to the ground, retracts and moves on to the next house. The process is very quick and efficient. (See the video further down.)

Watching the truck manhandle my trash container week after week I began wondering what g-forces were exerted on the trashcan by that giant claw? So I decided this would be a good Arduino data collection project.

Hardware

Microcontroller

The microcontroller used in the project was the Arduino (Atmega328) which I have been using in various other projects.

Data Logging

To store the data during collection I ordered and built the AdaFruit Datalogging shield. It has the ability to write data directly to an SD card as well as providing an on board Real Time Clock. Construction was easy with the clear instructions and code samples AdaFruit provides.

Accelerometer

Having never used an accelerometer, I spent some surfing time looking at different types and finally settled on SparkFun's item number SEN-00252 which is a Freescale triple-axis MMA7260QT.

I picked this one because it was a triple-axis sensor on a single IC, because it has selectable ranges of (+/-) 1.5g, 2g, 4g, and 6g, and because it comes on a handy breakout board. For this experiment I set the accelerometer up to have a sensitivity of 2g.


Construction

I decided to create my own simple accelerometer shield to plug into the data logging shield. This would give me flexibility to reuse the accelerometer for other projects later on.

The top view in the image below shows how I attached the accelerometer to a larger circuit board using a nylon standoff. I installed a couple of jumper blocks to let me change the sensitivity of the sensor and also added a switch that the Arduino could read to determine if logging should be running or paused. The bottom view shows the simple point to point solder connections I used.























Here is the schematic of the accelerometer shield.















In total the project has three boards, (clockwise from top left)
The Arduino, the Data Logger shield and the accelerometer shield.



















When assembled the unit looks like this

















Operation


Powering the Arduino with a 9V battery, I then put the sensor package into a small padded box and secured the box to the side of the trashcan with black duct tape.

I had two concerns about this part of the project. The first was that the sensor package would detach from the trashcan and my project would end up in the back of the trash truck, lost forever. Excessive use of duct tape was my attempt to prevent that from happening. My second concern was that the driver might see the sensor box as a “suspicious package” and call authorities. Fortunately neither fear was realized.






















And this image shows the X,Y and Z orientation of the final data collected by the experiment in relation to the trashcan.























Knowing the truck would arrive at my location a few minutes after it entered the neighborhood I waited until the truck was close, activated the Arduino and waited.








The truck and claw did their thing, the data logger and accelerometer did their thing and I was greatly relieved when my project survived intact and didn’t end up in some landfill.


Understanding the Data

An example of the data collected on the SD card is shown below. The first column is the current second of the Real Time Clock. I set up the software to record the X, Y and Z values eight times a second. The next three columns are the noise compensated voltage representations of the X, Y and Z outputs. The raw data is interesting but with a little post processing it can become more meaningful.

58 362.02 473.18 284.60
58 362.36 473.22 284.64
58 361.74 473.70 285.24
58 361.60 473.04 284.68
58 362.46 472.60 285.04
58 365.20 473.58 285.54
58 399.52 459.74 265.58
58 393.02 459.56 309.38
59 368.98 431.16 308.34
59 370.90 543.88 278.78
59 340.14 432.74 293.56

Noise compensation

I found that even just sitting stationary on my workbench the signals the accelerometer output were not noise free so I used a simple moving average algorithm in the Arduino code to compensate. It was simply a matter of taking 50 reading from each sensor and then averaging that value and writing it out to the card. (That is why the recorded values are a float numbers rather than the typical integer number the Arduino 10-bit analog to digital converter outputs.) This technique doesn’t remove all noise but it does smooth it out quite a bit.

All other post processing of the data was done by spreadsheet after the experiment was over.

Calibration

Since it was virtually impossible to orient the sensors to be exactly level in all three directions when attaching to the trashcan, it is important to use a calibration process to remove any tilt the sensors picked up as they sat quietly waiting for the truck.

To do this I averaged 1000 SD card readings for each axis as the sensor remained stationary. This value, converted into g-force (see formula description) when subtracted from each sample sample values (also converted into g-force) gave me a more accurate result of the forces on the trashcan.

Formula for Converting raw data to g-forces

I used the following formula to convert the raw data on the SD card to g force.

((Vref * (ADout / 1024)) – (Vdd / 2)) / Sensitivity

Vref = 5v and is the voltage reference used internally by the Arduino when doing analog to digital conversions.

ADout is the raw data (x, y or z) value recorded on the SD card.

1024 is the number of steps the Arduino 10-bit analog to digital converter splits a full voltage range into..

Vdd = 3.3v is the voltage input to the MMA7260QT accelerometer IC and it is divided by 2 because the sensor records both positive and negative forces. 3.3v/2 = 1.65v so values above 1.65v are positive force and values below 1.65v represent negative force.

Sensitivity is a value taken from the MMA7260QT data sheet. It represents how many mV/g the sensor will output based on the jumper settings inputs of the chip. I set my sensor to use a sensitivity of 2g so this value is 600 mV or 0.6v.

Formula Example

Say for example, from the SD card you find that the value on the X axis at some particular point in time is 602.64 Using the formula:

((5 * (602.62 / 1024)) – (3.3/2)) / 0.6 you get 2.15g the X axis.

The calibration value for the X axis (average of 1000 reading when the sensor was not moving) was 469.62 so using the same formula:

((5 * (469.62 / 1024)) - (3.3/2)) / 0.6 = 1.07 = X axis calibration value.

Subtracting the calibration value from the particular X reading gives the following 2.15g – 1.07g = 1.08g

This means that at that particular point in time, the trashcan was moved in a positive X direction with the force of 1.08g.

It is easy to setup a spreadsheet to list all the raw data, the calibration data for each axis and then have the spreadsheet run the conversion to g-force formula on each data value of raw input.

Finally, again using the spreadsheet, I graphed the g-forces on the trashcan in each of the X Y and Z directions as it was being emptied by the truck.




















Accelerometers are showing up in many devices this days; the Wii, iPhones, iPads, Droids, laptops etc. I hope this project encourages you to get your own accelerometer and perform other experiments.

Arduino Code


   
 /* --------------------------------------  
  MMA7260Q 3-axis accelerometer  
   
  Accelerometer and data collection.  
   
  code released as Open Source  
  feel free to use as you see fit.  
   
 */ --------------------------------------  
   
 // libraries from   
 // http://www.ladyada.net/make/logshield/download.html  
   
 #include <SdFat.h>  
 #include <Wire.h>  
 #include "RTClib.h"  
   
 #define ECHO_TO_SERIAL 0  // 1=echo is on, 0=echo is off  
   
 #define redLEDpin 9  
 #define greenLEDpin 8  
   
 #define x_axis 0  
 #define y_axis 1  
 #define z_axis 2  
 #define startStopSwitch 7  
   
   
 RTC_DS1307 RTC;   
 Sd2Card card;  
 SdVolume volume;  
 SdFile root;  
 SdFile file;  
   
 int sampleSize = 50; // used in moving average   
   
 //---------------------------------------------------------------  
 void error(char *str)  
 {  
  Serial.print("error: ");  
  Serial.println(str);  
  digitalWrite(greenLEDpin, LOW);   
  while(1)  
  {  
    digitalWrite(redLEDpin, HIGH);   
    delay(250);  
    digitalWrite(redLEDpin, LOW);   
    delay(250);     
  };  
 }  
   
 //---------------------------------------------------------------  
 void setup(void)  
 {  
  pinMode(redLEDpin, OUTPUT);  
  pinMode(greenLEDpin, OUTPUT);  
  pinMode(startStopSwitch, INPUT);  
    
  digitalWrite(redLEDpin, HIGH);  
  digitalWrite(greenLEDpin, HIGH);  
    
  Serial.begin(9600);  
   
  // initialize the SD card  
  if (!card.init()) error("card.init");  
    
  // initialize a FAT volume  
  if (!volume.init(card)) error("volume.init");  
    
  // open root directory  
  if (!root.openRoot(volume)) error("openRoot");  
    
  // create a new file  
  // starts with LOGGER00, and next one would be LOGGER01 if  
  // LOGGER00 already exists. THis preserves existing files and  
  // increments the new filename  
  char name[] = "XYZLOG00.CSV";  
  for (uint8_t i = 0; i < 100; i++)   
  {  
   name[6] = i/10 + '0';  
   name[7] = i%10 + '0';  
   //O_CREAT = create file, O_EXCL = only if file doesn't already exist  
   //O_WRITE = open for writing  
   if (file.open(root, name, O_CREAT | O_EXCL | O_WRITE)) break;  
  }  
    
  if (!file.isOpen()) error ("file.create");  
  //Serial.print("Logging to: ");  
  //Serial.println(name);  
   
  // write header  
  file.writeError = 0;  
   
  Wire.begin();   
  if (!RTC.begin())   
  {  
   file.println("RTC failed");  
   #if ECHO_TO_SERIAL  
    Serial.println("RTC failed");  
   #endif //ECHO_TO_SERIAL  
  }  
    
   
  file.println("sec,x-axis,y-axis,z-axis");    
  #if ECHO_TO_SERIAL  
   Serial.println("sec,x-axis,y-axis,z-axis");  
  #endif //ECHO_TO_SERIAL  
   
  // attempt to write out the header to the file  
  if (file.writeError || !file.sync()) {  
   error("write header");  
  }  
    
  digitalWrite(redLEDpin, LOW);  
  digitalWrite(greenLEDpin, LOW);  
  delay(1000);  
   
 }  
   
 //----------------------------------------------------------------------  
 void loop(void)  
 {  
    
  if (digitalRead(startStopSwitch) == LOW)  
  {  
   // user feedback for errors and status  
   digitalWrite(redLEDpin, HIGH);  
   digitalWrite(greenLEDpin, LOW);  
  }  
  else  
  {  
   digitalWrite(redLEDpin, LOW);  
   digitalWrite(greenLEDpin, HIGH);  
   DateTime now;  
    
   // clear print error  
   file.writeError = 0;  
    
   // delay for the amount of time we want between readings  
   delay(100);  
    
   digitalWrite(greenLEDpin, LOW);  
   now = RTC.now();   
   float x = 0;   
   float y = 0;   
   float z = 0;  
     
   // moving average for noise compensation  
   for( int i = 1; i<=sampleSize; i++)  
   {  
    x+= analogRead(0);  
    y+= analogRead(1);   
    z+= analogRead(2);  
   }  
   x = x / sampleSize;  
   y = y / sampleSize;   
   z = z / sampleSize;  
   
   // output format for CSV data on SD card    
   file.print(now.second(), DEC);  
   file.print(", ");  
   file.print(x);  
   file.print(", ");  
   file.print(y);  
   file.print(", ");  
   file.println(z);   
   #if ECHO_TO_SERIAL   
    Serial.print(now.second(), DEC);  
    Serial.print(", ");  
    Serial.print(x);  
    Serial.print(", ");  
    Serial.print(y);  
    Serial.print(", ");  
    Serial.println(z);    
   #endif //ECHO_TO_SERIAL   
    
   if (file.writeError) error("write data");   
   if (!file.sync()) error("sync");  
  }  
   
 }  
   
   

HexiLogger, an Arduino based data logger

The purpose of this project was to create a simple, portable device that would
periodically read sensors and then store the sensor data so it could be retrieved later.

The result is the HexiLogger, "hexi" because it can support up to six different sensor inputs and "logger" because it will store the input data on a removable SD memory card.

Hardware

The HexiLogger hardware consists of the following modules:

- ATmega328 microcontroller on a Diecimila Arduino board.
- AdaFruit GPS shield, minus the GPS module
- Homebrew user interface shield
- Sensors

I began the project using the ATmega168 chip that came with the Diecimila board but quickly discovered that the Fat16 libraries needed to support the SD memory card pushed the limit of the 168's flash RAM. Upgrading to the 328 chip was easy to do. Just swap the chips and then change the Arduino development environment to compile for the ATmega328 (Tools/Board). Doing that doubled the program space and stopped the strange behavior I was seeing witht eh 168 chip.

Stacked on the Arduino board is an AdaFruit GPS shield. I was only interested in using the shield's SD card, so there is no GPS module or supporting GPS code involved in this project. The only modifications I made to the GPS shield was to add a set of three female header pins to the top of the GPS shield for access to the Arduino analog inputs and data lines D0-D7.

Attached to the top of the GPS shield is a homebrew user interface shield that allows logging interval input, user feedback LEDs and connections for the sensors.

The sensors used are TMP36 from Analog Devices in a TO-92 package but any sensors that can be attached to an Arduino could be used.













This image shows the device in breadboard development phase with test sensors attached.













This image shows the GPS shield lifted off to view the SD card holder underneath.





















This image is the final configuration with all the breadboard hardware moved on to a custom shield that sits on top of the GPS shield.






Schematic















Software

The HexiLogger software consists of the following:

- Fat16 Libraries
- User interface supporting code
- Sensor support

Several libraries exist for Arduino/Fat16 interaction, and many of them are variations of each other. I tried a couple of different ones and settled on Fat16 because of its straight forward SD card support. Installation of the library is simple, just add it to arduino-0016/hardware/libraries folder and put #include "Fat16.h" and #include "SdCard.h" into the code. HexiLogger routines initializeCard() and writedataToCard() manage the storage file on the SD card.

The user interface switches are read by the HexiLogger function DetermineTimeDelay() which sets the interval that the sensors will be read. displayCardErrorIndicator(), displayRunIndicator() and
displayWriteIndicator() all handle the LED display.
More information about how the input switches are used to set the interval can be found in one of my other projects.

Each type of sensor will need its own software function in order to convert the raw data from the analog inputs into a meaningful value. I have included four, processLM35SensorDegF(), processTMP36SensorDegF(), processTMP36SensorDegC(),
and processPhotoCell() to handle some types of sensors that I used during development.

Operation Flow

The basic operation of the device is as follows:


1) When powered on, read the user input switches and determine the data logging interval.
2) Check the SD card.
if the card is missing or a file can't be created then display error indicator and stop.
if the SD card is operational, create a file and write out the file header.
3) Read the sensors, converting the data if necessary.
4) Open the file on the SD card, write the sensor data to the file.
5) Close the file.
6) Pause the required interval.
Go back to 3) and repeat.
Continue to loop through steps 3, 4, 5 and 6 until the power is removed.


Output Data File

The output file on the SD card will look like this:

Interval 10 minutes
a0,a1,a2,a3,a4,a5
59,57,0,0,0,0
56,56,0,0,0,0
55,57,0,0,0,0
55,57,0,0,0,0
55,57,0,0,0,0
56,58,0,0,0,0
etc.

The first two lines are the file header, showing the interval that the sensors were read and the sensor order in the CSV data. All the remaining lines are the sensor readings. The CSV format makes it easy to graph in a spreadsheet application.

Since HexiLogger is a simple device and does not use a Real Time Clock chip to determine the interval, there is no way to write out the time of the readings to each line in the file. However it would be a simple task to write a small PC program that takes as input the starting time, the SD Card file and sensor names and would produce additional columns like this.

Interval 10 minutes
time,inside,outside,n/a,na/,n/a,n/a
09:00,59,57,0,0,0,0
09:10,56,56,0,0,0,0
09:20,55,57,0,0,0,0
09:30,55,57,0,0,0,0
etc.

When I find some spare time I will write and post the code to such an application.

HexiLogger Test


To test the final project, I attached two TMP36 temperature sensors, set the interval to be 10 minutes and attached a new 9v battery to the HexiLogger.

One sensor was inside my vehicle and one sensor was outside under the vehicle in the shade.






I wanted to monitor the rise in temperature inside the closed vehicle as the outside air temperature rose throughout the day. Also I wanted to test to see how long the HexiLogger could run on a 9v battery.


I started the logger at 6:00am and let it run until the battery was dead. Below is a graph of the results.













Conclusion and Code

Running off a new 9v battery, HexiLogger was able to log data every 10 minutes for 8 hours. Not bad!
Changing the Arduino code to put the microcontroller into sleep mode between loggings might extend the battery life but unfortunately would prevent using delay() to manage the interval and would require more hardware, like a real time clock to trigger an interrupt. For now, I'll just use a different power supply if I need extended logging.

Arduino Code


/*
HexiLogger

* reads 6 analog inputs
* saves the data to SD card in Fat16 format
* pauses for specified interval
* repeat

Hardware base: Arduino ATmega328

This Arduino software is released to the public
domain "as is". The author makes no warranty
expressed or implied and assumes no responsibilities.
Feel free to fold, bend, spindle or mutilate as you see fit.

*/
//################################################################


#include "Fat16.h"
#include "SdCard.h"

SdCard card;
Fat16 file;

char name[] = "HLG-00.TXT";
int interval = 0;
unsigned long timeDelay = 0;

// user input switches for log interval
int switch_A = 7;
int switch_B = 6;
int switch_C = 5;
int switch_MS = 4;

// feedback LEDs
int greenLED = 3;
int yellowLED = 2;


float a0, a1, a2, a3, a4, a5;
int samplestaken = 0;
boolean cardError;
boolean useMinutes;

//------------------------------------------------
void displayCardErrorIndicator() {
digitalWrite(yellowLED, HIGH);
digitalWrite(greenLED, HIGH);
delay(500);
digitalWrite(yellowLED, LOW);
digitalWrite(greenLED, LOW);
delay(500);
}

//------------------------------------------------
void displayRunIndicator(){
digitalWrite(yellowLED, LOW);
digitalWrite(greenLED, HIGH);
}

//------------------------------------------------
void displayWriteIndicator(){
digitalWrite(yellowLED, HIGH);
digitalWrite(greenLED, LOW);
}

//------------------------------------------------
float processLM35SensorDegF(float rawIn){
return ((rawIn / 1024.0) * 5.0) * 100.0;
}

//------------------------------------------------
float processTMP36SensorDegF(float rawIn){
float v = rawIn * 5.0 / 1024;
// temperature C
float tc = (v - 0.5) * 100 ;
// temperature F
return (tc * 9 / 5) + 32;
}

//------------------------------------------------
float processTMP36SensorDegC(float rawIn){
float v = rawIn * 5.0 / 1024;
// temperature C
return (v - 0.5) * 100 ;
}

//------------------------------------------------
float processPhotoCell(float rawIn){
return (1024.0 - rawIn);
}

//------------------------------------------------
void getAnalogInputs(){
// Change depending on type of sensors attached

//a0 = processLM35SensorDegF(analogRead(0));
//a1 = processPhotoCell(analogRead(1));
a0 = processTMP36SensorDegF(analogRead(0));
a1 = processTMP36SensorDegF(analogRead(1));

// change when sensors attached
a2 = 0; // analogRead(2);
a3 = 0; // analogRead(3);
a4 = 0; //analogRead(4);
a5 = 0; //analogRead(5);
}

//------------------------------------------------
void setup() {
pinMode(switch_A, INPUT);
pinMode(switch_B, INPUT);
pinMode(switch_C, INPUT);
pinMode(switch_MS, INPUT);

pinMode(yellowLED, OUTPUT);
pinMode(greenLED, OUTPUT);

timeDelay = DetermineTimeDelay();

//5 second delay from power on to logging start
delay(5000);

//Serial.begin(9600);
initializeCard();
}

//------------------------------------------------
void loop() {

// ---- used for testing ---------------
//timeDelay = DetermineTimeDelay();
//initializeCard();
//Serial.println(timeDelay);
//delay(1500);
//--------------------------------------

if (cardError)
// blink both LEDs and do nothing else
displayCardErrorIndicator();
else {
// card and file ok, start logging
displayWriteIndicator();
getAnalogInputs();
writeDataToCard();
displayRunIndicator();
delay(timeDelay);
}
}

//--------------------------------------------------
void initializeCard() {
if (!card.init()) error("card.init");
if (!Fat16::init(card, 1))
if (!Fat16::init(card, 0))
error("Fat16::init");

for (int i = 0; i <> 15) {
file.sync();
samplestaken = 0;
}

if (file.isOpen())
file.close();
}

//---------------------------------------------------------
void error(char *str) {
Serial.print("error: ");
Serial.println(str);
cardError = true;
}

//--------------------------------------------------------
unsigned long DetermineTimeDelay(){
/* reads input switche bank and returns the number of milliseconds
switch_MS = 0 : seconds
swtich_MS = 1 : minutes

Switch
(CBA) Time
-----------------------
000 5
001 10
010 15
011 30
100 45
101 60
110 90
111 120
-----------------------

*/

boolean swA, swB, swC;
unsigned long timedelay = 0;

if (digitalRead(switch_MS) == HIGH)
useMinutes = true; // switch is up, up minutes
else
useMinutes = false; // switch is down, use seconds


if (digitalRead(switch_C) == HIGH)
swC = true;
else
swC = false;

if (digitalRead(switch_B) == HIGH)
swB = true;
else
swB = false;

if (digitalRead(switch_A) == HIGH)
swA = true;
else
swA = false;


if (!swC && !swB && !swA){
interval = 5;
if (!useMinutes)
timedelay = 5000;
else
timedelay = 300000;
}
else if (!swC && !swB && swA){
interval = 10;
if (!useMinutes)
timedelay = 10000;
else
timedelay = 600000;
}
else if (!swC && swB && !swA){
interval = 15;
if (!useMinutes)
timedelay = 15000;
else
timedelay = 900000;
}
else if (!swC && swB && swA){
interval = 30;
if (!useMinutes)
timedelay = 30000;
else
timedelay = 1800000;
}
else if (swC && !swB && !swA){
interval = 45;
if (!useMinutes)
timedelay = 45000;
else
timedelay = 2700000;
}
else if (swC && !swB && swA){
interval = 60;
if (!useMinutes)
timedelay = 60000;
else
timedelay = 3600000;
}
else if (swC && swB && !swA){
interval = 90;
if (!useMinutes)
timedelay = 90000;
else
timedelay = 5400000;
}
else if (swC && swB && swA){
interval = 120;
if (!useMinutes)
timedelay = 120000;
else
timedelay = 7200000;
}
else
// default to 1 min in case sw pack goes bad
timedelay = 600000;

return timedelay;
}

Daphne 2.0 a Houseplant on Twitter

Back in the old days, (mid 1990s) when the Internet was still a new thing for most people, there was this guy named Paul who hooked his hot tub to the Net. From a command prompt anywhere in the world you could type:
"finger hottub@hamjudo.com"
and this is what you would get in return:

"Paul's hottub is warm at about 99 degrees Fahrenheit.
It is nice outside at about 63 degrees Fahrenheit.
The ozone generator is working. The cover is closed.
The backup battery is OK at 10.5 volts (this will still work down to 6 volts)"

The hot tub, a coffee pot, a few Coke machines and of course a toaster were the early precursors to the myriad of things that are connected to the Internet today. In fact, we almost seem to be at the point where if a thing is not somehow connected to the Internet, then we are surprised.

Since I was wanting to explore the concept of serial communications, specifically a PC polling an Arduino for information, I decided to follow in Paul's footsteps and learn by putting something on the Internet. The result is Daphne 2.0, my house plant on Twitter. "Daphne" being both the name of the project and the name of the Dieffenbachia house plant who volunteered for this assignment.
As it turned out, this project was more about the software than the hardware.

Daphne's Twitter page is here.

General Description

With a multitude of different configurations and design available, I decided to stick to my "serial polling, tethered Arduino" plan and keep things as simple as possible.

Daphne Hardware

The hardware for Daphne is very simple; an Arduino, three sensors and a LED for blinking. Since this is just a temporary project I didn't even bother building an enclosure for the Arduino. I used one of my Bare-Bones Arduino clone boards and simply mounted it on a standard breadboard for stability. Power is supplied by the FTDI USB-to-TTL-Serial cable connected to the PC (more on PC-Arduino connections later).

The three things most important to Daphne were the things that were going to be monitored, temperature, light and moisture.

Sensors

For the temperature I used my favorite LM34 precision Fahrenheit temperature sensor. It has a range of -50F to +300F, output voltage linearly proportional to the temperature and can be operated without any trimming components. As the schematic below shows, connect one pin to +5v, one to ground and the third goes to the analog input A0 on the Arduino. This configuration provides the basic range of +5F to +300F. Can't get much simpler!

Light was measured using a photo-resistor (photocell) which reads about .5 ohms in full sun and about 40K ohms in darkness. The sensor was built by soldering a 10K ohm resistor in series with the photocell and adding a connection between the two that would be the analog input A1 to the Arduino. (see schematic).

Moisture is measured using two stainless steel probes in series with a 10k ohm resistor, and tapping in between the resistor and probs for analog input A2. The two probes are inserted into the plant's soil about 3 cm apart. The more moisture, the more current passes through the soil between these probes.

Each sensor needed +5v, a ground connection and an analog output line, so three wires ran from each sensor to to the Arduino. Fortunately the Bare-Bones Arduino is configured to make it easy for 3-wire connections into the analog inputs.

Other than an LED on pin D13 and its current limiting resistor, thats all there is to the hardware.

Daphne Software

*Note: Blogger does not allow file hosting so the code is listed at the bottom of this discussion.

The software consists of two separate applications, Arduino software and PC software. Both are discussed below.

Daphne / Arduino

The Arduino application in this project is simply an analog sensor manager. Its sole purpose is to provide the raw data from a particular sensor when asked. Looking through the code from the top you can see some constants for analog pin assignments, and some float variables are created and initialized. Then some variables to manage the blinking LED are set. This blinking is interesting because it does not use the delay() routine which can interfere with serial communications. A more detailed description of that blink code can be found here at the The Blink Without Delay page on the Arduino site.


The sampleSensor() routine takes an analog pin as input, samples the voltage a number of times with a slight delay in between samples and then returns the average value on that pin. The averaging reduces the data jitter found on many different types of sensors.

updateFeedback() is part of the Blink Without Delay code.

setup() is simple, just assign the LED pin and set the serial rate. The serial rate needs to be the same as your daphne PC application rate. For this simple program and the small amount of data received and transmitted, 9600 is plenty fast. In reality the sampleSensor() routine is where the slowdown occurs, not in the the serial communication.

The main loop() of the application works as follows: Arduino listens to its serial port and when data is available, it assigns that data to the sensorID variable. In this application Arduino is expecting either the characters "R", "a", "b" or "c". Anything else and Arduino just returns an "X", meaning it didn't understand what was asked. "R" means, Arduino, are you ready?, and is used by the PC program to determine if the Arduino is connected and powered on. If ready, Arduino returns a "Y". "a", "b", "c" are requests for the analog sensor data on analog pins 0, 1 and 2 respectively. For example, if Arduino sees an "a" on the serial input it calls sampleSensor(in_a) which gets and returns an average reading of A0 input.

Finally a call to updateFeedback() blinks the LED.

Thats it for the Arduino code. Very simple and easy to expand or modify. More analog sensors could be added or digital sensors as well.


Daphne / PC

Many different software applications can communicate with an Arduino.
Since this was a hardware and software education project for me I chose to write the PC portion of the Daphne project in Python and run it on a Ubuntu PC. The general logic of the Daphne PC application is this:

1) Daphne, are you available?
2) If yes, then get all sensor data.
(If Daphne not available, then print error message and End)
3) Once sensor data has been gathered, process that data.
4) Build some descriptions for each sensor.
5) Build a tweet
6) If in debug mode, print data to screen.
If not in debug mode, post the tweet up to Twitter.
7) End


The main() routine is where program logic starts. debugMode can be set to True or False depending on where you want to display the data. A data object called sensor_data is created. This object encapsulates all the raw and processed data within one run of the application. As an object, the data is then easier to pass around to different subroutines.

getSensorValues() takes the data object as a parameter, and for each sensor, sends out a serial request to the Arduino. the raw data is then stored in the data object.

processSensorvalues() is where the raw sensor data from the Arduino is massaged to become temperature, light and moisture data. The LM34 temperature sensor uses the formula ((s_data.tempF_Val / 1024) * 5) * 100 where s_data.tempF_Val is the raw Arduino data, 1024 is the maximum number of units that Arduino can slice the input voltage into, 5 is the maximum input voltage (5v) and 100 gets the value out of a decimal and turns it into a temp.

Temperature C is then computed. Then both the raw light value and the raw moisture value are subtracted from 1023 (max units again). This subtraction is done to generate a number that increases when light or moisture increases and decreases when light or moisture decrease. In other words, a light value of 835 represents more light falling on Daphne than a light value of 427. This subtraction in software is used to 'flip' the numbers because of the physical placement of the 10k ohm resistor in both the light and moisture sensor. If the resistors were on the ground side rather than the +5v side, then the subtraction would not be needed.

These calculated values are stored in the data object as well as scrapeData which is just a string of data in square brackets. More on this later.

Next, buildSensorDescriptions() is called. This is the "fuzzy logic" portion of Daphne where I did a little testing to decide what the sensor values meant to Daphne. Temperature was easy, above 80 is "toasty", around 50 is "cool", etc. Light and moisture were determined by logging the extremes (full sun, total darkness, 100% water, no water at all) and splitting and assigning names like "bright" and "moist" to sub ranges. Not completely precise but that fuzzy logic for you.

buildTwitterTweet() puts all the pieces of information into a string that will fit into the required Twitter string length.

Finally, if debugMode is True, then the information is just printed locally to the PC screen, otherwise sendTwitterInformation() is called and the tweet is posted. If you use the code, provide your own user name and password.

The result on Daphne_Plant's Twitter page is something like this:

"The temperature is a toasty 81.1F degrees (27.3C), the light is bright and my soil is moist. [tlm,81.1,981.0,721.0]"

The information in the square brackets is the scrapeData mentioned earlier. It represents the temperature, light, moisture data values and is included so I can scrape the data off all the Twitter posts with a different Python application, effectively using Twitter as my data storage device (but thats a different blog entry sometime.)


PC to Arduino Connections

This was always to be a tethered project from the start, meaning the Arduino was going to only be responsible for answering polls and providing data serially, through some form of connection to a PC. Daphne has been tested in three different tether configurations; PC to Bare-Bones with FTDI USB-to-TTL-Serial cable, PC to Diecimila Arduino with USB cable and PC to Diecimila Arduino via Ethernet using XPort Direct shield and XPort module.

On order are a set of XBee's and shields. Who knows, maybe awireless Daphne project update be available someday.


Daphne Arduino Code


/*
  Daphne_v2_0

  * Manages 3 analog sensors as a polled response.
  * Includes blinking LED as ready state feedback.
  * Returns raw sensor data, the calling software is responsible
    for interpretation of the raw data

Hardware base: Arduino AtMega168

This Arduino software is released to the public
domain "as is". The author makes no warranty
expressed or implied and assumes no responsibilities. 
Feel free to fold, bend, spindle or mutilate as you see fit.

*/
//################################################################

// analog sensor input pin assignments
const int in_a = 0;  
const int in_b = 1;  
const int in_c = 2;  

// feedback blinking LED
const int feedbackLED = 13;
int feedbackLED_state = LOW;
long feedbackPreviousMillis = 0;
// once per second blink
long feedbackInterval = 1000;
float sampleCount = 5.0;

float a_val = 0;
float b_val = 0;
float c_val = 0;

//-----------------------------------------------------------------------
int sampleSensor(int sensorPin) {
  // samples the sensor n times for an average reading

  float s = 0.0;
  for(int i=0; i    s += analogRead(sensorPin);
    delay(200);
  } 
 
  return s / sampleCount;  
}

//-----------------------------------------------------------------------
void updateFeedback() {
  // feedback LED blinker
  if (millis() - feedbackPreviousMillis > feedbackInterval) {
    feedbackPreviousMillis = millis();  
   
    if (feedbackLED_state == LOW)
      feedbackLED_state = HIGH;
    else
      feedbackLED_state = LOW;

    // set the LED with the ledState of the variable:
    digitalWrite(feedbackLED, feedbackLED_state);
  } 
}

//-----------------------------------------------------------------------
void setup() {
  pinMode(feedbackLED, OUTPUT);  
  Serial.begin(9600);  
}

//-----------------------------------------------------------------------
void loop() {
 
  byte sensorID;
 
  // loop until a request comes in
  if (Serial.available() > 0) { 
    sensorID = Serial.read();

    switch(sensorID) {
      case 'R':                 // are you Ready arduino ?
        Serial.println('Y');    // respond, Yes
        break; 
       
      case 'a':     // analog 0         
        a_val = sampleSensor(in_a);
        Serial.println(a_val);   
        break;
       
      case 'b':     // analog 1          
        b_val = sampleSensor(in_b);
        Serial.println(b_val);
        break;
          
      case 'c':    // analog 2           
        c_val = sampleSensor(in_c);
        Serial.println(c_val);
        break;  
       
      default:
        Serial.println("X"); 
    }
  }
 
  updateFeedback();
  
}   



Daphne Python Code


#!/usr/bin/python
#
#  Daphne_v2_0
#
#  * Manages 3 analog sensors as a polled response.
#  * Includes blinking LED as ready state feedback.
#  * Returns raw sensor data, the calling software is responsible
#    for interpretation of the raw data
#
#
# This Python software is released to the public
# domain "as is". The author makes no warranty
# expressed or implied and assumes no responsibilities. 
# Feel free to fold, bend, spindle or mutilate as you see fit.
#
################################################################

import os
import sys
import time
import serial
import twitter
import simplejson

usbport = '/dev/ttyUSB0'
ser = serial.Serial(usbport, 9600, timeout=500)

#------------------------------------------------------------------------------
class SensorDataPackage:

    # data description
    d_date = "YY/MM/DD"
    d_time = "Time"
    d_tempf = "Temp(F)"
    d_tempc = "Temp(C)"
    d_light = "Light"
    d_moisture = "Moisture"
    d_twitter = "Tweet"

    # date and time strings
    ds = ""
    ts = ""

    tempF_raw = 0   # temperature
    light_raw = 0   # luminosity
    moisture_raw = 0   # moisture

    tempF_Val = 0
    tempC_Val = 0
    light_Val = 0
    moisture_Val = 0
    tweet_Val = ""

    tempDescription = ""
    lightDescription = ""
    moistureDescription = ""
    scrapeData = ""

#------------------------------------------------------------------------------
def arduinoAvailable():

    # are you available?
    ser.write('R')
    answer = ser.readline()
    #print answer
    if answer.strip() == 'Y':
        return True
    else:
        return False

#----------------------------------------------------------------------------
def getSensorValues(s_data):
   
    s_data.ds = time.strftime("%y/%m/%d", time.localtime())  # date
    s_data.ts = time.strftime("%H:%M:%S", time.localtime())  # time

    # temperature
    ser.write('a')
    s_data.tempF_raw = ser.readline().strip()

    # light
    ser.write('b')
    s_data.light_raw = ser.readline().strip()

    # moisture
    ser.write('c')
    s_data.moisture_raw = ser.readline().strip()

#----------------------------------------------------------------------------
def processSensorValues(s_data):

    #value to temperature F
    s_data.tempF_Val = float(s_data.tempF_raw)
    s_data.tempF_Val = ((s_data.tempF_Val / 1024) * 5) * 100;

    # for debugging
    s_data.tempC_Val = 5.0/9.0 * (s_data.tempF_Val - 32)

    #luminosity  (sensor produces the inverse, so reverse it here) {lower number = darker}
    s_data.light_Val = float(s_data.light_raw)
    s_data.light_Val = 1023 - s_data.light_Val

    #soil moisture level (sensor produces the inverse, so reverse it here) {lower number = dryer}
    s_data.moisture_Val = float(s_data.moisture_raw)
    s_data.moisture_Val = 1023 - s_data.moisture_Val

    # build scrape data
    s_data.scrapeData = "[tlm," + '%.1f'%(s_data.tempF_Val) + "," + '%.1f'%(s_data.light_Val) + "," + '%.1f'%(s_data.moisture_Val) + "]"


#----------------------------------------------------------------------------
def buildSensorDescriptions(s_data):

    # temperature
    if s_data.tempF_Val >= 80:
        s_data.tempDescription = "toasty"

    elif ((s_data.tempF_Val >= 70) and (s_data.tempF_Val <= 79.9)):
        s_data.tempDescription = "pleasant"

    elif ((s_data.tempF_Val >= 60) and (s_data.tempF_Val <= 69.9)):
        s_data.tempDescription = "cool"

    elif ((s_data.tempF_Val >= 50) and (s_data.tempF_Val <= 59.9)):
        s_data.tempDescription = "cool"

    elif ((s_data.tempF_Val >= 40) and (s_data.tempF_Val <= 49.9)):
        s_data.tempDescription = "cold"

    else:
        s_data.tempDescription = "freezing"   


    # light
    if s_data.light_Val >= 1010:
        s_data.lightDescription = "intense"

    elif ((s_data.light_Val >= 950) and (s_data.light_Val <= 1009.9)):
        s_data.lightDescription = "bright"

    elif ((s_data.light_Val >= 500) and (s_data.light_Val <= 949.9)):
        s_data.lightDescription = "fading"

    elif ((s_data.light_Val >= 200) and (s_data.light_Val <= 499.9)):
        s_data.lightDescription = "dim"

    else:
        s_data.lightDescription = "off"


    # moisture
    if s_data.moisture_Val >= 1000:
        s_data.moistureDescription = "wet"

    elif ((s_data.moisture_Val >= 700) and (s_data.moisture_Val <= 999.9)):
        s_data.moistureDescription = "moist"

    else:
        s_data.moistureDescription = "Dry! Hello, I said DRY!!!" 


#----------------------------------------------------------------------------
def screenPrintInformation(s_data):

    # data dump to screen
    timeNow = time.strftime("%H:%M:%S", time.localtime())

    print
    print s_data.d_time, ":", "\t", "\t", timeNow
    print s_data.d_tempf, ":",  "\t", '%.1f'%(s_data.tempF_Val), "\t", "\t", s_data.tempDescription
    print s_data.d_tempc, ":",  "\t", '%.1f'%(s_data.tempC_Val)
    print s_data.d_light, ":",  "\t", '%.1f'%(s_data.light_Val), "\t", "\t", s_data.lightDescription
    print s_data.d_moisture, ":",  "\t", '%.1f'%(s_data.moisture_Val), "\t", "\t", s_data.moistureDescription
    print "Scrape data", ":", "\t\t", s_data.scrapeData
    print s_data.d_twitter, ":",  "\t", s_data.tweet_Val
    print "Length of tweet is: ", len(s_data.tweet_Val)
    print


#----------------------------------------------------------------------------
def buildTwitterTweet(s_data):

    #tempDesc = "Currently it is a " + s_data.tempDescription + " " + '%.1f'%(s_data.tempF_Val) + "(F) degrees, "
    tempFDesc = "The temperature is a " + s_data.tempDescription + " " + '%.1f'%(s_data.tempF_Val) + "F degrees "
    tempCDesc = "(" + '%.1f'%(s_data.tempC_Val) + "C), "
    lightDesc = "the light is " + s_data.lightDescription
    moistureDesc = " and my soil is " + s_data.moistureDescription + ".   "
   
    #s_data.tweet_Val = tempDesc + lightDesc + moistureDesc
    s_data.tweet_Val = tempFDesc + tempCDesc + lightDesc + moistureDesc + s_data.scrapeData

#----------------------------------------------------------------------------
def sendTwitterInformation(s_data):

    try:
        api = twitter.Api(username="your_name", password="your_password")
        api.PostUpdate(s_data.tweet_Val)
        #print "tweet ok"
    except ValueError:
        print "Error"

#----------------------------------------------------------------------------
def main():

    # True for screen dump, False for Twitter post
    debugMode = True

    sensor_data = SensorDataPackage()
 
    if arduinoAvailable():
        #print "Arduino is available"
        getSensorValues(sensor_data)
        processSensorValues(sensor_data)
        buildSensorDescriptions(sensor_data)
        buildTwitterTweet(sensor_data)
        if debugMode:
            screenPrintInformation(sensor_data)
        else:
            sendTwitterInformation(sensor_data)
    else:
        print "No response, Arduino may not be connected."       
   
    sys.exit(0)
   
#-----------------------------------------------------------------------------

# >>> entry point when application starts
if __name__ == '__main__':
    main()
else:
    sys.exit(0)




YAPI 2.0 Yet Another Programmable Intervalometer

This project was born out of a need for a device to trigger the wake/focus and shutter actions of a Canon Digital Rebel XT camera at predetermined intervals. I wanted this device for time-lapse photography to be something simple in terms of both hardware and software yet flexible enough to be easily reconfigurable if necessary.

The result is "YAPI" or "Yet Another Programmable Intervalometer", so named because devices like this seem to be popular home brew projects as any Google search will show. This article will describe my version of the the hardware and software.

The overall operation of YAPI is simple:

1) YAPI is connected to the remote focus/shutter port on the camera.
2) The switch pack is set to the desired interval.
3) Power is supplied to the micro-controller.

As soon as power is applied, YAPI immediately triggers the shutter, determines the user selectable duration and then begins a countdown to the next shutter event. This loop continues until power is removed.

General Description

The core hardware component behind YAPI is an Arduino based micro-controller. This project uses a Bare-Bones Arduino clone from Modern Device but any of the other Arduino product configurations could easily be adapted by making the appropriate data line connections.

The Bare-Bones Arduino clone itself came as a kit and was very easy to put together. 17 male pins extend below the Bare-Bones and slide into a 17-pin female connector/riser that is soldered to the main YAIP board.

This connector/riser provides access to all Arduino's digital lines as well as +5v and ground. Additionally this configuration provides an open area under the Bare-Bones for future hardware expansion as well as allowing the Bare-Bones to be removed for other uses if the need arises.








YAPI Hardware Description


The YAPI board connects a small 4x switch pack to Arduino pins D2, D3, D4 and D5 which are configured as inputs. Four 10K ohm resistors pull these pins to ground so unless a switch is in the 'on' position, Arduino will see a 0 or LOW input on these
pins. The switches are used to select the shutter trigger interval.

What the switch setting mean in terms of duration and action is completely up to the software in the Arduino. In other words a switch setting could mean "take a picture every five minutes" or by simply changing the software the same switch setting could mean "take 10 pictures in a row, wait an hour and take 3 more pictures". That is the "programmable" part of YAPI.

The camera wake/focus and shutter are activated by Arduino pins D7 and D12 which are configured as outputs. When Arduino wants to wake up and focus the camera for example, it will present a HIGH output on pin D7. D7 is connected to the input pin of a TIL111 optoisolator. Just about any optoisolator should work, I used the TIL111 only because I had a few laying around. A 470 ohm current limiting resistor on another pin of the optoisolator provides a path to ground. This lets Arduino turn on the internal phototransistor which has the effect of activating the reed relay and also electrically isolates Arduino from the relay as well as the camera.

The reed relay is a small 0.5A relay I found at Radio Shack and in this configuration acts a simple continuity switch connecting the focus signal from the camera to camera common. This is the same behavior as someone manually pressing the shutter button
on the camera itself part way down to make the camera auto-focus.

A second set of optoisolator and relay are connected to Arduino pin D12 and behaves the same way but is connecting the shutter signal from the camera to camera common. This is the action that actually takes the picture.

I added a 1N914 protection diode to each relay in order to prevent the voltage spike from the relay from damaging the phototransistor in the optoisolator. Maybe the diodes were not really needed at these low voltage levels but since they were so easy to attach directly to the top of the relays I went ahead and put them in.

Two LEDs with current limiting 470 ohm resistors are connected to Arduino pins D9 and D10 which are configured as outputs. These are for user feedback to show when the focus and the shutter are active. Besides, every gadget needs blinky lights! The LED actions could easily be reconfigured in software for another purpose.

All hardware is mounted on a small perf-board, with optoisolators mounted in in sockets. All the components are connected with wires via point to point soldering on the back of the perf-board.

Power for the unit is fed directly into the Bare-Bones board and can be supplied from a 9v wall transformer (wall-wart) or a 9v battery. I haven't done any tests to see how long YAPI can run on a battery, but for any extended operation a plug in wall-wart would be the way to go.

The result is a compact device with an overall size of about 3 3/4 inches x 2 3/4 inches x 1 inch (9.5 cm x 7cm x 2.5cm).

Arduino Software Description

Note: Sadly, Blogger won't allow me post the source code as an actual file so I'll just display the full listing at the end of this discussion.

The general logic flow of the software is as follows:
1. read the user input from the switches
2. take action ( auto-focus and trip the shutter)
3. pause for the duration
4. go back and begin again

Currently the software is coded to trigger the camera at 5, 10, 15, 30, 45, 60, 90, or 120 intervals with those intervals either seconds or minutes, based on one of the input switch setting.

Other home-brew intervalometers I have seen use an LCD screen, selection buttons, debounce code and additional software to make a wide range of duration selections. Thats quite nice but I have found that I typically only needed a small set of durations in my photography. I also wanted to keep this project as simple as possible.

As mentioned in the hardware section above, duration selection for the intervalometer is set through the physical switches attached to input pins D2, D3, D4 and D5. As Arduino runs in its main loop() routine it calls DetermineTimeDelay() to check the switches and get the delay duration. More on this routine in a minute.

Arduino then calls triggerWakeFocus() which outputs a HIGH on pin 7, causing the relay to close, waking up the camera if it was asleep and activating focus. After a slight delay of 10 microseconds the the triggerShutter() routine activates the camera shutter with a HIGH on output pin 12. Each of these two routines also turn on and then off their respective LED for some visual feedback.

That 10 microsecond delay between wake/focus and shutter serves two purposes. First, gives the camera time to actually wake up and focus and second it allows Arduino to active both relays in short order. I found that without the delay, only one relay would fire and I have no idea why. The 10ms delay solved that strange problem.

After triggering the shutter, Arduino goes into it's delay() routine for the required time. I elected to use the Arduino internal delay(milliseconds) routine rather than add a Real Time Clock (RTC) chip like the DS1307 for a few reasons. First, without an LCD display and addition input buttons, setting the time on the clock chip would be a problem. Second, I don't really need the kind of accuracy a RTC provides since this isn't a NASA moon landing after all. Besides, even if YAPI loses or gains a few seconds over the course of a day of shooting photos that is close enough for me. Perhaps someday I'll set up YAPI to take time-lapse shots of a clock over 24 hours to check the actual accuracy of using delay().

That DetermineTimeDelay() routine at the beginning of loop() is the "programmable" part of YAPI. With four input switches I decided to use a binary coding of on/off to make 16 different durations available. One of the switches is assigned to be a
"minutes or seconds" selection and the other three switches participate in the following 8 durations:

Switch 1 is for minutes / seconds


3 remaining Resulting
switches duration
000 5
001 10
010 15
011 30
100 45
101 60
110 90
111 120


For example, if switch 1 off (meaning, "use seconds") and the three remaining switches are "off-on-off" (010) then the duration is determined to be 15 seconds.

If switch 1 is on (meaning "use minutes") and the three remaining switches are off-on-off (010) then the durations is determined to be 15 minutes.

The "If (swC && swB && swA)" statements in the remaining part of the DetermineTimeDelay() routine assign a predetermined milliseconds count to the value that will be returned and used in the delay() routine. For example:

Seconds (duration * 1000)
--------------------------------------------------
time value return for delay(ms)
010 = 15 15000


Minutes (duration * 60,000)
--------------------------------------------------
time value return for delay(ms)
010 = 15 900000

It is there in the "if (swC && swB && swA)" statememts that code can easily be modified to have YAPI do different things depending on the switch selection.

Conclusion

I hope this description explains the YAPI project, how it works and how it can be taken and easily modified. Interesting modifications I have considered are analog input triggers such as motion, sound or light beam interruption. Even altitude or sudden accelerations changes (using someone else's camera of course!) Digital triggers like door switches, pressure gauges or water level sensors could also be used to trigger a photograph.

YAPI (Arduino) source code


/*

YAIP 2.0 Yet Another Programmable Intervalometer

Hardware base: Arduino AtMega168

This Arduino software is released to the public
domain "as is". The author makes no warranty
expressed or implied and assumes no responsibilities.
Feel free to fold, bend, spindle or mutilate as you see fit.

*/
//################################################################

// user input switches
int switch_A = 2;
int switch_B = 3;
int switch_C = 4;
int switch_MS = 5; // minutes, seconds selection

// camera activity pins
int wake_focus = 7;
int shutter = 12;

// feedback LEDs
int redWakeFocusLED = 9;
int greenShutterLED = 10;

unsigned long timeDelay = 0; // used in Delay(timeDelay);

// adjust these as needed
int wakeFocusDelay = 500;
int shutterDelay = 500;
int cameraProcessingDelay = 500;

//--------------------------------------------------------
void setup(){

pinMode(switch_A, INPUT);
pinMode(switch_B, INPUT);
pinMode(switch_C, INPUT);
pinMode(switch_MS, INPUT);

pinMode(wake_focus, OUTPUT);
pinMode(shutter, OUTPUT);

pinMode(redWakeFocusLED, OUTPUT);
pinMode(greenShutterLED, OUTPUT);
}

//--------------------------------------------------------
void loop() {
// read the user's input switches each loop
timeDelay = DetermineTimeDelay();

// activate the camera
// need a delay in between two relay calls otherwise
// one of the relay relay won't trigger
triggerWakeFocus();
delay(10);
triggerShutter();

// give the camera time to process and store the image
delay(cameraProcessingDelay);

// pause user input switch duration
delay(timeDelay);
}

//--------------------------------------------------------
void triggerWakeFocus(){

digitalWrite(redWakeFocusLED, HIGH);
digitalWrite(wake_focus, HIGH);
delay(wakeFocusDelay);
digitalWrite(wake_focus, LOW);
digitalWrite(redWakeFocusLED, LOW);
}

//--------------------------------------------------------
void triggerShutter(){

digitalWrite(greenShutterLED, HIGH);
digitalWrite(shutter, HIGH);
delay(shutterDelay);
digitalWrite(shutter, LOW);
digitalWrite(greenShutterLED, LOW);
}

//--------------------------------------------------------
unsigned long DetermineTimeDelay(){
// reads input switches and returns the number of milliseconds

boolean swA, swB, swC, use_minutes;
unsigned long result = 0;

// read the switches and gather their state
if (digitalRead(switch_MS) == HIGH)
use_minutes = true; // switch is up
else
use_minutes = false; // switch is down, use seconds instead

if (digitalRead(switch_C) == HIGH)
swC = true;
else
swC = false;

if (digitalRead(switch_B) == HIGH)
swB = true;
else
swB = false;

if (digitalRead(switch_A) == HIGH)
swA = true;
else
swA = false;


// based on swtich state, determine determin the delay
if (!swC && !swB && !swA){
//result = 5;
if (!use_minutes)
result = 5000;
else
result = 300000;
}
else if (!swC && !swB && swA){
//result = 10;
if (!use_minutes)
result = 10000;
else
result = 600000;
}
else if (!swC && swB && !swA){
//result = 15;
if (!use_minutes)
result = 15000;
else
result = 900000;
}
else if (!swC && swB && swA){
//result = 30;
if (!use_minutes)
result = 30000;
else
result = 1800000;
}
else if (swC && !swB && !swA){
//result = 45;
if (!use_minutes)
result = 45000;
else
result = 2700000;
}
else if (swC && !swB && swA){
//result = 60;
if (!use_minutes)
result = 60000;
else
result = 3600000;
}
else if (swC && swB && !swA){
//result = 90;
if (!use_minutes)
result = 90000;
else
result = 5400000;
}
else if (swC && swB && swA){
//result = 120;
if (!use_minutes)
result = 120000;
else
result = 7200000;
}
else
result = 0;

return result;
}