rocketnumbernine

Andrew's Project Blog: Hardware, Software, Stuff I find Interesting

This is a description of my first useful Arduino project - The Finger.

The external USB drive that's a backup device to my Mac Mini media server doesn't cope well with being on 24/7. The power supply has had to be replaced once and the disk gets (comparatively) noisy which is annoying as it sits in my living room under the TV.

The solution was something that would turn the disk on and off when needed. Something controlled by an Arduino communicating with a perl script on the Mac seemed a fun way of doing it.

Which Arduino.

I used a Seeeduino Arduino compatible board, Seeeduino Boardmainly because the Arduino Duemilanove Boards were out of stock when buying. But I've grown quite fond of the Seeeduino, the SMD components and mini USB make it look much more compact and looks neat, the ability to turn of auto reset functionality by switch has been very useful. For prototyping the Sparkfun ProtoShield board fits snugly on top.

Pushing the Switch

I didn't want to take the disk apart or play with mains electricity so it had to be turned on mechanically by pushing the on/off switch. A servo motor seemed the best choice as they can be easily controlled from the Arduino with no extra electronics and can be picked up fairly cheaply compared with linear actuators. A Servo was also much easier to mount and house on the front of the disk.

Wiring is straightforward: brown cable to Arduino GND pin, red to +5V, and orange to a digital out - D9.Testing the Servo Arm ActionThe test standalone sweep program is below was useful for testing the mechanical assembly and finding out the required amount of movement - it just sweeps the arm a few degrees, stops for a set period and then sweeps back to neutral position.

If moving slowly enough with little resistance, the servo only drew a max of about 150mA , so can be powered safely from the USB supplied 5V available off the Arduino.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <Servo.h> 

int SERVOPIN = 9;
Servo servo;

void setup() 
{ 
  servo.attach(SERVOPIN);
} 

// sweep the servo from 0 to final holding
// for hold milliseconds before returning to 0
void servoSweep(int final, int hold)
{
  for(int pos = 0; pos < final; pos += 1) 
  {
    servo.write(pos);
    delay(20);
  }
  delay(hold);
  for(int pos = final; pos >= 0; pos -= 1) {
    servo.write(pos);
    delay(20);
  }
}

// sweep the servo then wait for 5 seconds
void loop() 
{ 
  servoSweep(20, 3000); 
  delay(5000);
}

Detecting state of the box

Unfortunately the disk only indicates it's on by the light emanating from the grill on the front. But it's trivial to connect up a photoresistor to an analogue pin to detect it. The photoresistor was put in series with a resistor to form a voltage divider with the midpoint feeding into Analogue pin A0 as shown in the following schematic:

Schematic for photoresistor and servo connections

Schematic for photoresistor and servo connections

Testing the PhotoresistorI found the program below - which reads the value of the analogue input and sends it to the serial/USB device (which can be viewed from the serial monitor in the Arduino IDE) - together with a multimeter useful for calculating a useful value for the lower resistor.

1
2
3
4
5
6
7
8
9
10
11
12
void setup() 
{ 
  beginSerial(19200);
} 
 
// send the value of A0 to the serial
// every half second
void loop() 
{ 
  Serial.println(analogRead(0));
  delay(500);
}

Putting It All together.

I found a plastic box that the Seeeduino board fits snuggly in (with its battery socket removed and a bit of filing on the corners) with enough room for the servo.Strip Board There wasn't much room for anything else so the extra components went on a piece of strip board connected to the left side header strip (where it gets 5V,GND, and A0) with a header for the servo socket and wire to connect to the digital pin D9 on the other side of the board.HousingThe photoresistor is positioned at the edge of the hole for the servo arm and gets enough light from the disk.

Video closeup of the device is below:

The final Arduino program is shown below, it waits for one of two commands from the USB, either "status" which will make it reply with the current status of the disk ("on" or "off") or "sweep" which will make it depress the button (to turn the disk on or off) and then return the sampled status.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// servo control/analogue sampling - use as you wish
// by andrew@rocketnumbernine.com 2009
#include <Servo.h>

int ANALOGUE = 0;
int SERVOPIN = 13;
int BUFSIZE=80;
char cBuf[80];
int i=0;
Servo servo;

char * getStatus()
{
  int val = analogRead(ANALOGUE);
  if (val > 5) {
    return "on";
  } else {
    return "off";
  }
}

void sendStatus()
{
  Serial.println(getStatus());
}

void servoSweep(int finalPosition, int holdDuration)
{
  for(int pos = 0; pos < finalPosition; pos += 1) {
    servo.write(pos);
    delay(20);
  }
  delay(holdDuration);
  for(int pos = finalPosition; pos > 0; pos -= 1) {
    servo.write(pos);
    delay(20);
  }
}

void processMessage (char *message)
{
  if (strcmp(message, "status") == 0) {
    sendStatus();
  } else if (strcmp(message, "sweep") == 0) {
    servoSweep(30, 3000);
    delay(1000);
    sendStatus();
  } else {
    char temp[80] =  "unknown command ";
    strcat(temp, message);      
    Serial.println(temp);        
  } 
}

void setup()
{
  pinMode(SERVOPIN, OUTPUT);
  servo.attach(9);  
  servo.write(0);
  Serial.begin(19200);
}

void loop()
{
  getStatus();
  if (Serial.available () > 0) {
    // read the incoming byte:
    cBuf[i] = Serial.read();
    if (i==BUFSIZE-1 || cBuf[i]<41 || cBuf[i]>176) {
      cBuf[i] = NULL;
      processMessage(cBuf);
      i=0;
    } else {
      i++;
    }  
  }
}

I used perl on the mac to send the commands to the Arduino (I'm sure this can be done in 3 lines of ruby, but I know perl). Usage is fairly straightforward:

1
2
3
4
5
6
7
8
# disk-control status
device off
# disk-control off   
device reports it is off, use -force off to change anyway
# disk-control on
device on
# disk-control status
device on

Finally the perl source is below - it uses Device::SerialPort which may needed to be installed ("cpan Device::SerialPort").

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#! /usr/bin/perl
# Arduino control perl script - use as you wish
# by andrew@rocketnumbernine.com 2009

use Device::SerialPort;

$SERIAL="/dev/tty.usbserial-A9009rh1";
$USAGE="usage: disk-control.pl [-force] [on|off]\n";

# connect to USB as serial
sub serialConnect
{
    my $port = Device::SerialPort->new($SERIAL);
    $port->databits(8);
    $port->baudrate(19200);
    $port->parity("none");
    $port->stopbits(1);
    return $port;
}

# wait for arduino to send something
sub readSerial
{
    while ($i<10) {
        $data = $port->lookfor();
        if ($data) {
            chomp($data);
            return $data;
        } else {
            sleep 1;
            $i++;
        }
    }
    return "timeout waiting for finger\n";
}

# get the current status of the device ('on'/'off')
sub getStatus
{
    $port->write("status\n");
    $data = readSerial();
    $data =~ s/[^\w]*//g;
    return $data;
}

# change the status of the device
# checks current status and only calls sweep if 
# different from desired or $force has been set
sub changeStatus
{
    my $desiredState = shift;
    my $status = getStatus();
    if (($desiredState eq $status) && !$force) {
        print "device reports it is $status, use "
        . "-force $desiredState to change anyway\n"; 
        exit
    } else {
        $port->write("sweep\n");
    }
}

$port = serialConnect();

$message = shift ARGV;
if ($message =~ /-force/) {
    $force = true;
    $message = shift ARGV;
}

# either try to change status
if ($message =~ /on|off/) {
    changeStatus($message);
} else { # or request current status
    $port->write("status\n");
}

print "device " . readSerial() . "\n";
$port->close();

It looks like your browser doesn't support javascript so comments won't work

Tags/Categories: projects, arduino, seeeduino, servo, photoresistor, control