Friday, December 24, 2010

Nintendo DS Touchscreen Arduino Servo Turret



Here we describe how to set up a full-range 3-servo turret controlled by a Nintendo DS touchscreen and Arduino Duemilanove with an ATMega328 microcontroller. The magnitude of the distance from the center of the touchscreen to the touched position will control the angle of the servo on top for elevation, while the azimuth, or rotation of the ground plane, will be controlled by basically polar coordinates on the DS touchscreen.

It's just a demo; I did this to become familiar with Arduino microcontrollers. You can mount a bb gun controlled by a simple switch. Making it wireless may be one of my next projects.

One of the things that make the Arduino really easy to use is the libraries. These are basically sets of functions that achieve a certain category of tasks. In this project we use the "Servo" library. Information on this library can be found here.

Below is the code. The ranges of movement corresponding to the ranges of touch can be easily adjusted by changing the constants defined at the beginning, so you can make the turret easier to use.

Circuit details:
--See code comments for connections to the touchscreen (X1,Y2,X2,Y1,Xin,Yin).
--Connect the servos (HT-311) to an appropriate power source (red to positive, black to negative).
--Group the signal wires (yellow) of the azimuth (ground-plane) servos together and connect them to digital pin 11.
--Connect the elevation servo signal wire to digital pin 12.
--Be sure to connect the ground of the arduino to the ground of the servo power supply. I do not know why this is necessary, haha, and it took me a while to figure out.
--That's it! Here's an Arduino Reference, if you need it.

//Feel free to use this code however you like, but please credit lordvon and this site!
//Code was adapted from that found on (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1243499684/0).
#include <Servo.h>

Servo azimuth;
Servo elevation;

// Empirically determined; Voltage between an output high and output low pin.
#define Vref 4.46
#define highpulse 2400
#define lowpulse 600
#define quarter (highpulse-lowpulse)/4
#define s1high lowpulse+quarter/2
#define s2high s1high+quarter
#define s3high s2high+quarter
#define s4high s3high+quarter
#define sdivide .7071
#define maxmag 420

//When looking at DS screen with connector bottom left,
// the connections are from left to right:
//TOP, X1
//LEFT, Y2
//BOTTOM, X2
//RIGHT, Y1

// Digital pin connections (used to drive power)
#define X1 2 // TOP to Digital output 5
#define Y2 3 // LEFT to digital output 2
#define X2 4 // BOTTOM to digital output 3
#define Y1 5 // RIGHT to digital output 4

// Analog inputs
#define Xin 3 // From X1
#define Yin 4 // From Y1

// set initial touched position
int touchX;
int touchY;
int xpos;
int ypos;
float cosine;
float sine;
float mag;
int apulse;
int epulse;

void setup()
{
azimuth.attach(11);
elevation.attach(12);
}

void loop()
{
if (touched())
{
xpos = touchX - 511;
ypos = touchY - 511;
if(!(xpos == 0 and ypos == 0))
{
mag = sqrt(pow(ypos,2) + pow(xpos,2));
sine = ypos / mag;
if(abs(sine)>=sdivide)
{
cosine = xpos / mag;
if(ypos>0)
{
apulse = map(cosine*10000,-7071,7071,s2high,s3high);
}
else
{
if(xpos >= 0)
{
apulse = map(cosine*10000,7071,0,s4high,highpulse);
}
else
{
apulse = map(cosine*10000,0,-7071,lowpulse,s1high);
}
}
}
else
{
if(xpos>0)
{
apulse = map(sine*10000,7071,-7071,s3high,s4high);
}
else
{
apulse = map(sine*10000,-7071,7071,s1high,s2high);
}
}
}
azimuth.writeMicroseconds(apulse);
if(mag>maxmag or mag<0)
{
mag=maxmag/2;
}
epulse = map(mag,0,maxmag,highpulse,lowpulse);
elevation.writeMicroseconds(epulse);
}
}

boolean touched()
{
boolean touch = false;
// Horizontal routine Y1:Y2::Vcc:Gnd
pinMode(Y2, OUTPUT);
digitalWrite(Y2, LOW);
pinMode(Y1, OUTPUT);
digitalWrite(Y1, HIGH);
// X1,X2 high impedance (input mode)
pinMode(X1, INPUT);
pinMode(X2, INPUT);
// wait a bit, then read from X1
delay(10);
touchX = analogRead(Xin);
// Vertical routine X1:X2::Vcc:Gnd
pinMode(X2, OUTPUT);
digitalWrite(X2, LOW);
pinMode(X1, OUTPUT);
digitalWrite(X1, HIGH);
// Y1,Y2 high impedance (input mode)
pinMode(Y1, INPUT);
pinMode(Y2, INPUT);
// wait a bit, then read from Y1
delay(10);
touchY = analogRead(Yin);

// Only read touch if coords are below 1000 and above 0
// (stops errors with certain screens)
if(touchX < 1023 and touchX > 0 and touchY < 1023 and touchY > 0)
{
touch = true;
}

return touch;
}

No comments:

Post a Comment