The source code
#include <SoftwareServo.h>
SoftwareServo distanceServo; // create servo object to control a servo
SoftwareServo panServo; // create servo object to control a servo
int roomMap[60]; //array to save distances of the room
int roomFuzz[60]; //array to save the fuzzy-ness of each location in the room map
int roomTrys[60]; // number of tries it took to get a good measurement for this position
int n = 0;
int oldA = 0;
byte counter = 0;
int temp;
int menuPage = 0; //saves which page the user is on.
// 0 = Arm Turret
// 1 = SetScanRange
// 2 = Set Number of Shots
// 3 = Set Cool Down Period
//------OPTIONS ------//
int panMin = 60;
int panMax = 120;
int numOfShots = 1; //number of shots to shoot at the target
int coolDownTime = 10; //seconds to wait for target to fall on the floor after being shot
//------PINS ------//
//int potPin = 0; // select the input pin for the potentiometer
int distSensorPin = 0; //Ultrasonic distance sensor - analog pin 0
int encoderPinA = 6;
int encoderPinB = 7;
int encoderPushBtnPin = 5;
int reedSwitchPin = 4;
int redBtnPin = 3;
int speakerPin = 2;
int motionSensorPin = 8; //PIR motion sensor
int firingMotorPin = 9; //motor that shots the bullets
int triggerMotorPin = 10;
int panServoPin = 11;
int tiltServoPin = 12;
int distServoPin = 15;
void setup()
{
pinMode(speakerPin, OUTPUT);
pinMode(firingMotorPin, OUTPUT);
pinMode(triggerMotorPin, OUTPUT);
pinMode(redBtnPin, INPUT);
pinMode(encoderPinA, INPUT);
pinMode(encoderPinB, INPUT);
pinMode(encoderPushBtnPin, INPUT);
pinMode(reedSwitchPin, INPUT);
pinMode(motionSensorPin, INPUT);
digitalWrite(encoderPushBtnPin, HIGH); // turn on pullup resistor
digitalWrite(redBtnPin, HIGH); // turn on pullup resistor
digitalWrite(encoderPinA, HIGH); // turn on pullup resistor
digitalWrite(encoderPinB, HIGH); // turn on pullup resistor
digitalWrite(reedSwitchPin, HIGH); // turn on pullup resistor
digitalWrite(motionSensorPin, HIGH); // turn on pullup resistor
distanceServo.attach(distServoPin); // attaches the servo on pin 9 to the servo object
distanceServo.setMinimumPulse(700);
distanceServo.setMaximumPulse(2100);
panServo.setMaximumPulse(2000);
Serial.begin(9600);
Serial.println("ok, im ready");
delay(2000);
clearLCD();
printMenu(menuPage);
backlightOn();
delay(200);
backlightOff();
delay(200);
backlightOn();
delay(200);
backlightOff();
delay(200);
backlightOn();
playNote('d', 200);
playNote('c', 150);
}
void loop()
{
//main menu
switch (getButtonPress(false)) { //false means dont keep the servos active
case 0:
menuPage++;
menuPage = constrain(menuPage, 0, 5);
printMenu(menuPage);
break;
case 1:
menuPage--;
menuPage = constrain(menuPage, 0, 5);
printMenu(menuPage);
break;
case 2:
if(menuPage == 0){ //if arm turret
attackMode();
printMenu(menuPage);
}else if(menuPage == 1){ //if set scan limits
setScanRangeProcedure();
printMenu(menuPage);
}else if(menuPage == 2){ //if set number of shots
setNumShotsProcedure();
printMenu(menuPage);
}else if(menuPage == 3){ //if Set Cool Down Period
setCoolDownTimeProcedure();
printMenu(menuPage);
}else if(menuPage == 4){ //if test fire
panServo.attach(panServoPin); //so the pan servo doesnt jerk around
fire();
panServo.detach();
}else if(menuPage == 5){ //if booby trap mode
boobyTrapMode();
menuPage=0;
printMenu(menuPage);
}
break;
case 3:
break;
}
} //loop
int findTarget(){
clearLCD(); Serial.print("scanning for"); selectLineTwo(); Serial.print("target....");
unsigned long distance;
int threshold;
int counter;
int pos;
int val;
//distanceServo.write(0);
//delay(2000);
for(int i = 0; i < 2; i++){ //scan twice
pos = panMin;
counter = 0;
while(pos < panMax){
distanceServo.write(pos);
delay(10);
SoftwareServo::refresh();
counter++;
if(counter > 10){
counter = 0;
pos = pos + 3;
val = pos; //get pan angle
val = map(val, 0,180,0,50);
val = constrain(val, 0, 50); // limits range of sensor values to between 10 and 50
distance = getDistance(1); //get distance in inches
threshold = roomMap[val] - roomFuzz[val] - roomTrys[val]*10 - 5;
if(threshold < 0) { threshold = 0; } //bandage fix.
if( distance < threshold ) {
distance = getDistance(5); //get distance in inches
if( distance < threshold ) { //double check
//target found
Serial.println("Target found");
Serial.print(val);
Serial.print(",");
Serial.println(distance);
Serial.print("threshold = ");
Serial.println(threshold);
return (pos);
}
}
}//if
}//while
}//for
return 0;
}//findTarget()
void mapOutTheRoom(){
clearLCD();
Serial.print("Mapping out room...");
//clear everything
for(int i = 0; i < 60; i += 1){
roomMap[i] = 0;
roomFuzz[i] = 0;
roomTrys[i] = 0;
}
unsigned long distance;
int counter = 0;
int pos = panMin;
int val = 0;
int largestVal = 0;
int temp[3];
for (int i = 0; i < 20; i++){
distanceServo.write(pos);
SoftwareServo::refresh();
delay(20);
}
while(pos < panMax){
distanceServo.write(pos);
delay(10);
SoftwareServo::refresh();
counter++;
if(counter > 10){
counter = 0;
pos = pos + 3;
val = pos; //get pan angle
getDistanceWithErrorBars(4,temp);
//distance = getDistance(4); //get distance in inches
val = map(val, 0,180,0,50);
val = constrain(val, 0, 50); // limits range of sensor values to between 10 and 50
if (val > largestVal) { largestVal = val; }
roomMap[val] = temp[0];
roomFuzz[val] = temp[1];
roomTrys[val] = temp[2];
Serial.print(val);
Serial.print(",");
Serial.print(roomMap[val]);
Serial.print(",");
Serial.print(roomFuzz[val]);
Serial.print(",");
Serial.println(roomTrys[val]);
}//if
}//while
Serial.print("largestVal: ");
Serial.println(largestVal);
}//mapOutTheRoom
//returns the average distance taken over the specified number of measurements
int getDistance(byte measurements){
//if(measurements > 9) {return 0;}
int minDist = 5000;
int maxDist =0;
int avgDist = 0;
int temp = 0;
do{
minDist = 5000;
maxDist = 0;
for (int i = 0; i < measurements; i++){
do{
temp = (analogRead(distSensorPin) / 2) ; // read distance sensor
}while(temp < 12); //reject random low values
avgDist = avgDist + temp;
if (temp < minDist) { minDist = temp; }
if (temp > maxDist) { maxDist = temp; }
delay(50);
}//for
avgDist = avgDist / measurements; // average
if(maxDist - minDist > 30){
playNote('f',50);
}
}while(maxDist - minDist > 30);
//dist = dist / 2; // scale down to inches
return avgDist;
}//getDistance
//returns the average distance taken over the specified number of measurements, the error and number of trys it took to get a good measurement
void getDistanceWithErrorBars(int measurements, int result[]){
//if(measurements > 9) {return 0;}
int minDist = 5000;
int maxDist =0;
int avgDist = 0;
int temp = 0;
int numOfTrys = -1;
//do{
numOfTrys++;
minDist = 5000;
maxDist = 0;
for (int i = 0; i < measurements; i++){
do{
temp = (analogRead(distSensorPin) / 2) ; // read distance sensor
}while(temp < 12); //reject random 6's
avgDist = avgDist + temp;
if (temp < minDist) { minDist = temp; }
if (temp > maxDist) { maxDist = temp; }
delay(50);
}
avgDist = avgDist / measurements; // average
if(maxDist - minDist > 30){
playNote('f',50);
}
//}while(maxDist - minDist > 30);
//result[0] = avgDist;
result[0] = minDist;
result[1] = maxDist - minDist;
result[2] = numOfTrys;
//dist = dist / 2; // scale down to inches
}//getDistanceWithErrorBars
void playTone(int tone, int duration) {
for (long i = 0; i < duration * 1000L; i += tone * 2) {
digitalWrite(speakerPin, HIGH);
delayMicroseconds(tone);
digitalWrite(speakerPin, LOW);
delayMicroseconds(tone);
}
}
void playNote(char note, int duration) {
char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
// play the tone corresponding to the note name
for (int i = 0; i < 8; i++) {
if (names[i] == note) {
playTone(tones[i], duration);
}
}
}
void selectLineOne(){ //puts the cursor at line 0 char 0.
Serial.print(0xFE, BYTE); //command flag
Serial.print(128, BYTE); //position
}
void selectLineTwo(){ //puts the cursor at line 0 char 0.
Serial.print(0xFE, BYTE); //command flag
Serial.print(192, BYTE); //position
}
void goTo(int position) { //position = line 1: 0-15, line 2: 16-31, 31+ defaults back to 0
if (position<16){ Serial.print(0xFE, BYTE); //command flag
Serial.print((position+128), BYTE); //position
}else if (position<32){Serial.print(0xFE, BYTE); //command flag
Serial.print((position+48+128), BYTE); //position
} else { goTo(0); }
}
void clearLCD(){
Serial.print(0xFE, BYTE); //command flag
Serial.print(0x01, BYTE); //clear command.
}
void backlightOn(){ //turns on the backlight
Serial.print(0x7C, BYTE); //command flag for backlight stuff
Serial.print(157, BYTE); //light level.
//Serial.print(0x7C, BYTE); //command flag for backlight stuff
//Serial.print(0x0C, BYTE); //light level.
}
void backlightOff(){ //turns off the backlight
Serial.print(0x7C, BYTE); //command flag for backlight stuff
Serial.print(128, BYTE); //light level for off.
}
/* waits for a button press and returns which button was pressed:
0 - encoder up
1 - encoder down
2 - encoder push
3 - button push
*/
int getButtonPress(boolean keepServosActive ){
while( true ){
if(keepServosActive) {SoftwareServo::refresh(); } // keep the servo active
//check button
if (digitalRead(redBtnPin) == LOW) {
delay(50); //debounce button
while(digitalRead(redBtnPin) == LOW){ delay(1); } //wait for button to be released
return 3;
}
if (digitalRead(encoderPushBtnPin) == LOW) {
delay(50); //debounce button
while(digitalRead(encoderPushBtnPin) == LOW){ delay(1); } //wait for button to be released
return 2;
}
//check encoder
n = digitalRead(encoderPinA);
if ((oldA == LOW) & (n == HIGH)) {
oldA = n;
if (digitalRead(encoderPinB) == LOW) {
return 0;
} else {
return 1;
}
}
if ((oldA == HIGH ) & (n == LOW)) {
oldA = n;
if (digitalRead(encoderPinB) == LOW) {
return 1;
} else {
return 0;
}
}
oldA = n;
}//while
}//getButtonPress()
// 0 = Arm
// 1 = SetScanRange
// 2 = Set Number of Shots
// 3 = Set Cool Down Period
// 4 = test fire
// 5 = Booby Trap Mode
void printMenu(int page){
if(page == 0){
clearLCD();
Serial.print("Arm Turret");
}else if(page == 1){
clearLCD();
Serial.print("SetScanRange");
}else if(page == 2){
clearLCD();
Serial.print("Set Number of");
selectLineTwo();
Serial.print("Shots");
}else if(page == 3){
clearLCD();
Serial.print("Set Cool Down");
selectLineTwo();
Serial.print("Period");
}else if(page == 4){
clearLCD();
Serial.print("Test Fire");
}else if(page == 5){
clearLCD();
Serial.print("Booby Trap Mode");
}
}//printMenu
void setScanRangeProcedure(){
clearLCD(); Serial.print("Right Limit:"); selectLineTwo(); Serial.print(panMin); Serial.print(" ");
byte btnPressed;
panServo.attach(panServoPin);
distanceServo.write(panMin);
panServo.write(panMin);
do{
btnPressed = getButtonPress(true); //pass true to keep servos active
if(btnPressed == 1) { //if encoder up
panMin++; //future work: make sure the left limit doesnt pass the right limit
panMin = constrain(panMin, 0, 180);
selectLineTwo();
Serial.print(panMin); Serial.print(" ");
distanceServo.write(panMin);
panServo.write(panMin); //debug stuff
}
if(btnPressed == 0) { //if encoder down
panMin--;
selectLineTwo();
panMin = constrain(panMin, 0, 180);
Serial.print(panMin); Serial.print(" ");
distanceServo.write(panMin);
panServo.write(panMin); //debug stuff
}
SoftwareServo::refresh();
}while(btnPressed != 3 & btnPressed != 2); //while cancel and confirm buttons are not pushed
clearLCD();
Serial.print("Left Limit:");
selectLineTwo();
Serial.print(panMax); Serial.print(" ");
distanceServo.write(panMax);
do{
btnPressed = getButtonPress(true); //pass true to keep servos active
if(btnPressed == 1) { //if encoder up
panMax++;
panMax = constrain(panMax, 0, 180);
selectLineTwo();
Serial.print(panMax); Serial.print(" ");
distanceServo.write(panMax);
panServo.write(panMax); //debug stuff
}
if(btnPressed == 0) { //if encoder down
panMax--;
selectLineTwo();
panMax = constrain(panMax, 0, 180);
Serial.print(panMax); Serial.print(" ");
distanceServo.write(panMax);
panServo.write(panMax); //debug stuff
}
SoftwareServo::refresh();
}while(btnPressed != 3 & btnPressed != 2); //while cancel and confirm buttons are not pushed
panServo.detach();
menuPage = 0;
return;
}//setScanRangeProcedure()
void setNumShotsProcedure(){
//TODO: if cancel button is pressed restore old settings
byte btnPressed;
clearLCD();
Serial.print("Number of shots:");
selectLineTwo();
Serial.print(numOfShots); Serial.print(" ");
do{
btnPressed = getButtonPress(false); //false means dont keep the servos active
if(btnPressed == 0) { //if encoder up
numOfShots++; //future work: make sure the left limit doesnt pass the right limit
numOfShots = constrain(numOfShots, 1, 20);
selectLineTwo();
Serial.print(numOfShots); Serial.print(" ");
}
if(btnPressed == 1) { //if encoder down
numOfShots--;
selectLineTwo();
numOfShots = constrain(numOfShots, 1,20);
Serial.print(numOfShots); Serial.print(" ");
}
}while(btnPressed != 3 & btnPressed != 2); //while cancel and confirm buttons are not pushed
menuPage = 0;
return;
}//setNumShotsProcedure()
void setCoolDownTimeProcedure(){
//TODO: if cancel button is pressed restore old settings
byte btnPressed;
clearLCD();
Serial.print("Seconds To Wait:");
selectLineTwo();
Serial.print(coolDownTime); Serial.print(" ");
do{
btnPressed = getButtonPress(false); //false means dont keep the servos active
if(btnPressed == 0) { //if encoder up
coolDownTime++; //future work: make sure the left limit doesnt pass the right limit
coolDownTime = constrain(coolDownTime, 1, 60);
selectLineTwo();
Serial.print(coolDownTime); Serial.print(" ");
}
if(btnPressed == 1) { //if encoder down
coolDownTime--;
selectLineTwo();
coolDownTime = constrain(coolDownTime, 1,60);
Serial.print(coolDownTime); Serial.print(" ");
}
}while(btnPressed != 3 & btnPressed != 2); //while cancel and confirm buttons are not pushed
menuPage = 0;
return;
}//setNumShotsProcedure()
void fire(){
digitalWrite(firingMotorPin,HIGH);
delay(1000);
digitalWrite(triggerMotorPin,HIGH);
for(int i = 0; i < numOfShots; i++){
while(digitalRead(reedSwitchPin) == 0){ delay(2); } //wait for the trigger to move away
delay(200);
while(digitalRead(reedSwitchPin) == 1){ delay(2); } //wait for the trigger to come back
delay(20);
}
digitalWrite(triggerMotorPin,LOW);
digitalWrite(firingMotorPin,LOW);
}//fire()
void pullTrigger(){
//digitalWrite(firingMotorPin,HIGH);
delay(500);
digitalWrite(triggerMotorPin,HIGH);
for(int i = 0; i < numOfShots; i++){
while(digitalRead(reedSwitchPin) == 0){ delay(2); } //wait for the trigger to move away
delay(200);
while(digitalRead(reedSwitchPin) == 1){ delay(2); } //wait for the trigger to come back
delay(20);
}
digitalWrite(triggerMotorPin,LOW);
digitalWrite(firingMotorPin,LOW);
}//pullTrigger()
void slowPan(int location){
panServo.attach(panServoPin);
panServo.write(location); // sets the servo position according to the scaled value
for(int i = 0; i < 40; i++){
panServo.write(location);
delay(20); // waits for the servo to get there
SoftwareServo::refresh();
}
panServo.detach();
}//slowPan
int waitForMotion(){
// 1 = no motion
// 0 = motion
while( digitalRead(motionSensorPin) == 0){ //while motion
delay(200); //wait for sensor to report no motion
}
delay(500);
while( digitalRead(motionSensorPin) == 1 ){ //while no motion
delay(10); //wait for motion
if(digitalRead(redBtnPin) == 0){
return 0; //cancel button was pressed
}
}//while
return 1;
}//waitForMotion()
void attackMode(){
unsigned long timeA;
unsigned long timeB;
mapOutTheRoom();
while(true){
temp = waitForMotion();
if(temp == 0){
clearLCD(); Serial.print(" Turret is now"); selectLineTwo(); Serial.print(" **DISARMED**");
delay(1200);
return;
} //if cancel button was pressed while waiting for next target
temp = findTarget();
if(temp > 0){ //if target was found
digitalWrite(firingMotorPin,HIGH); //spin up motors
slowPan(temp);
pullTrigger();
timeA = millis();
timeB = timeA + coolDownTime*1000;
//cool down
while(timeB > timeA){
timeA = millis();
if(digitalRead(redBtnPin) == 0){
clearLCD(); Serial.print(" Turret is now"); selectLineTwo(); Serial.print(" **DISARMED**");
delay(1200);
return; //cancel button was pressed
}//if
}//while
}//if
clearLCD(); Serial.print("Waiting for"); selectLineTwo(); Serial.print("next target.");
}//while
}//atackMode()
void boobyTrapMode(){
clearLCD(); Serial.print("Waiting for"); selectLineTwo(); Serial.print("victim...");
unsigned long timeA;
unsigned long timeB;
while(true){
temp = waitForMotion();
if(temp == 0){ //if cancel button was pressed while waiting for next target
clearLCD(); Serial.print(" Turret is now"); selectLineTwo(); Serial.print(" **DISARMED**");
delay(1200);
return;
} //if
fire();
//cool down
timeA = millis();
timeB = timeA + coolDownTime*1000;
while(timeB > timeA){
timeA = millis();
if(digitalRead(redBtnPin) == 0){
clearLCD(); Serial.print(" Turret is now"); selectLineTwo(); Serial.print(" **DISARMED**");
delay(1200);
return; //cancel button was pressed
}//if
}//while
}//while
}//boobyTrapMode