Spirobot
Side View of body and control panel |
Front View: Zip Tie holding the front |
Wheels: Servo attachment connected with screws |
Back: Servo mounted with screws |
Back: Marker attachment clip shown |
Inside: Servos on sides, Arduino board, and circuitry |
Arduino board circutry |
Code:
#include <Servo.h>
/* SpiroBot
*
* By: Felix Sotres
* for: Art 150
* Last update: 5/1/14
*
* Description: Spirobot draws 3 different patterns which are generated randomly. It can also switch colors while its drawing the pattern and increases the size of the pattern randomly.
*
* Note: Please excuse some of the commented code, the plan was originally to run this via Processing for Arduino, but due to a defective Bluetooth dongle, it was not possible and this project was ported over to Sketch for Arduino.
*/
/* Global Variables */
// Servo Objects
Servo markerServo;
Servo leftServo;
Servo rightServo;
char movesArray[] = {‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘x’};
int currMove = 0; //marker for movesArray
// Variables for commands
char currComm = ‘ ‘; //holds current command
int num = 99; //holds command from user
int swipes = 1; //number of times command will be repeated in Spirograph Mode
int sqr = 3; //square side length val
int tri = 2.5; //triangle side length val
float growth = 0.0; //amount that sides length will grow by
int randComm1 = 0; //random command
int randComm2 = 0; //random command
int randComm3 = 0; //random command
int randComm4 = 0; //random command
int indent = 60; //indentation
int consoleIn = 0; //holds commands for testing
// Menu flags
boolean start = true; //indicates user started program
boolean intro = true; //indicates start of program
boolean pathMode = false; //indicates Spirobot is in Path Mode
boolean spiroMode = false; //indicates Spirobot is in Spirograph Mode
boolean demoMode = true; //indicates Spirobot is in Demo Mode
boolean drawBot = false; //tell system to draw the bot or not
boolean bottomMssg = false; //unlocks bottom menu
// Action flags
boolean swapColor = false; //used for switching colors
// Variables for dealy
int totalTime = 3000; //total time to wait (3 secs), program delay only
int savedTime; //variable to hold time
int passedTime = 0; //time elapsed
int moveDel = 2000; //amount of seconds Spirobot will move (2 secs by default)
int turnDel = 1000; //amount of seconds Spirobot will turn
//int y = 10; //location of text
/* setup()
*
* – Setup all paramaters for program
* – Runs once during lifecycle of program
*/
void setup() {
Serial.begin(9600); //open serial port
// Configure digital pins 9, 10, and 11 to control servo motors.
leftServo.attach(9); //cont rotation
rightServo.attach(10); //cont rotation
markerServo.attach(11); //reg servo
// Configure digital pin 4 and 13 for LED indicator
pinMode(4, OUTPUT);
pinMode(13, OUTPUT);
// Configure digital pin 2 for button input
pinMode(2, INPUT);
//stop both servos from moving by default
leftServo.write(1510);
rightServo.write(1510);
// set servo at 90 by default
markerServo.write(90);
Serial.print(“servo shoulda moved”);
//turn LED indicator lights off by default
digitalWrite(13, LOW);
digitalWrite(4, LOW);
//save time at start of program
savedTime = millis();
}
/* loop()
* – Runs program
* – Called automatically and should never be called explicitly.
*/
void loop() {
//tasks to do at beginning, once
if(start == true){
robotStop();
markerServo.write(90);
//wait for button push
readyState();
//indicate its no longer start of program
start = false;
}
//get number of swipes
swipes = getRandom(millis(), 3, 7);
//get length of sides
sqr = getRandom(millis(), 2, 4);
//get amount side length will grow by
growth = getRandom(millis(), 1, 7);
growth = growth/10;
//draw square x times (swipes)
for(int i=0; i<swipes; i++){
greenLED(true);
// Swap the color
switchColor();
drawSquare(sqr, sqr);
sqr= sqr+ growth;
}
greenLED(false);
//do a finishing dance
finishedYay();
//pause for push of button
readyState();
//get variables for triangle
tri = getRandom(millis(), 1, 5);
growth = getRandom(millis(), 2, 6);
growth = growth / 10;
swipes = getRandom(millis(), 4, 8);
//draw triangle
for(int i=0; i<swipes; i++){
greenLED(true);
// Swap the color
switchColor();
drawTriangle(tri);
tri = tri+ growth;
}
greenLED(false);
//do a finishing dance
finishedYay();
//wait for push button
readyState();
//get variables for random command pattern
randComm1 = getRandom(millis(), 0, 6);
Serial.println(“commands: “);
Serial.print(randComm1);
delay(5);
randComm2 = getRandom(millis(), 0, 6);
Serial.print(randComm2);
delay(3);
randComm3 = getRandom(millis(), 0, 6);
Serial.print(randComm3);
delay(7);
randComm4 = getRandom(millis(), 0, 6);
Serial.print(randComm4);
swipes = getRandom(millis(), 2, 6);
Serial.print(” swipes: “);
Serial.print(swipes);
//execute random command x times (swipes)
for(int i = 0; i<swipes; i++){
greenLED(true);
// Swap the color
switchColor();
commandExec(randComm1);
commandExec(randComm2);
commandExec(randComm3);
commandExec(randComm4);
}
greenLED(false);
//do a finishing dance
finishedYay();
//wait for push button to start all over
readyState();
//if received input from console execute command (testing)
if(Serial.available() > 0){
//read incoming int
consoleIn = Serial.read();
//print to console
Serial.print(“Received: “);
Serial.println(consoleIn, 0 );
//execute command
commandExec(consoleIn);
}
}
/*********************
* SPIROBOT PATTERNS *
**********************/
/* drawSquare(int, int)
*
* – draws a square with values for x and y axis
*/
void drawSquare(int x, int y){
int a = x * 1000; //set up seconds to go in x axis
int b = y * 1000; //set up seconds to go in y axis
int turn = 800; //rotate for 0.8 secs
//draw the square
for(int i=0; i < 2; i++){
robotFwd(b); //straight line y axis
rotateRight(turn); //rotate 90 deg
robotFwd(a); //straight line x axis
rotateRight(turn); //rotate 90 deg
}
}
/* drawTriangle(int)
*
* – draws a trinagle (sharper angles)
* – int is the length of the sides of the triangle
*/
void drawTriangle(int x){
x = x* 1000; //set up seconds to go straight
int turn = 1270;
//draw the shape
robotFwd(x);
rotateLeft(turn);
robotFwd(x);
rotateLeft(turn);
robotFwd(x);
rotateLeft(turn);
}
/* getRandom(long, int, int)
*
* – returns a random integer number
*/
int getRandom(long seed, int minVal, int maxVal){
int val = 0;
randomSeed(seed);
val = random(minVal, maxVal);
return val;
}
/*****************
* GUI COMMANDS *
******************/
void addMoveMidMssg(char c){
//println(“inside addMoveMidMssg(char)”); //testing
//path mode is true
if(pathMode == true){
//can store up to 10 moves
if(currMove < 11){
//add first move with no comma
//if( midMssg == “Moves: “){
//midMssg = midMssg + ” ” + c;
//}else{
//add every other move with a comma
//midMssg = midMssg + ” ,” + c;
//if 10 moves total notify user its full
//if(currMove == 10){
//midMssg = midMssg + ” FULL”;
//}
//}
}
}
//spirograph mode is true
if(spiroMode == true){
//can store up to 10 moves
if(currMove < 6){
//add first move with no comma
//if( midMssg == “Moves: “){
//midMssg = midMssg + ” ” + c;
//}else{
//add every other move with a comma
//midMssg = midMssg + ” ,” + c;
//if 5 moves total notify user its full
//if(currMove == 5){
//midMssg = midMssg + ” FULL”;
//}
//}
}
}
//println(midMssg); //testing
}
/* exitProg()
*
* – exits program whenever user presses x
*/
void exitProg(){
//closing commands [ end() closes serial connection http://arduino.cc/en/Serial/End )
//text(“exiting…”, 40, 40);
//delayT();
//exit();
}
/* resetVals()
*
* – resets all values in program
*/
void resetVals(){
//clear screen
//clearScreen();
// reset flags
start = true; //indicates user started program
intro = true; //indicates start of program
pathMode = false; //indicates Spirobot is in Path Mode
spiroMode = false; //indicates Spirobot is in Spirograph Mode
demoMode = false; //indicates Spirobot is in Demo Mode
drawBot = false; //tell system to draw the bot or not
bottomMssg = false; //unlocks bottom menu
//reset curr moves
currMove= 0; //reset current move location
//reset moves array
for(int i= 0; i<11; i++){
//print(“resetting movesArray pos: “);
//println(i);
movesArray[i] = ‘ ‘;
}
movesArray[10] = ‘x’; //reset end marker
}
/* echoKey(char)
*
* – Displays the character on the screen
* – Typically the input from the user
*/
void echoKey(char k){
//fill(0);
//text(k, indent, 110);
Serial.print(k);
}
/***************
* TIME DELAYS *
****************/
/* delayT()
*
* – Delays the program for 3 seconds (default)
*/
void delayT(){
//current time minus the last saved time
passedTime = millis() – savedTime;
//do while the diff between the current time and last saved time is less than the delay wanted
while(passedTime < totalTime){
//update the time that has passed
passedTime = millis() – savedTime;
}
Serial.println(“end of delay”); //testing purposes
// Save the current time to restart the timer
savedTime = millis();
//background(random(255)); //redraw background
//text(“end of delay”, indent, 160);
//text(millis(), indent+50, y);
//y++;
}
/* delayT(int)
*
* – Accepts an int and delays the program for that amt of time
* – Delays the program for 3 seconds (default)
* – Used for Spirobot movement
*/
void delayT(int del){
//current time minus the last saved time
passedTime = millis() – savedTime;
//do while the diff between the current time and last saved time is less than the delay wanted
while(passedTime < del){
//update the time that has passed
passedTime = millis() – savedTime;
}
Serial.println(“end of movement delay”); //testing purposes
// Save the current time to restart the timer
savedTime = millis();
}
/******************
* INPUT CONTROLS *
*******************/
/* commandExec(int)
*
* – main program used to execute all commands
* – main control function
* – the 3 different modes (path, spirograph, demo) have been seperated for possible expansion
*/
void commandExec(int com){
switch(com){
/*
case ‘d’:
//if no moves were entered display message
if(currMove == 0){
//setMidMssg(“Moves: No moves added! Please add at least 1 move.”);
fill(237, 43, 82);
text(“No moves added! Please add at least 1 move.”, 40, 160);
}else{
// mark end of list in moves array
currMove++; //needed because of the way addMove(char) works
addMove(‘x’); //add end of array marker to movesArray
currMove = 11; //stops other moves from being added
pauseMusic();
playReady(); //play sound “ready?”
readyState(); //ready state waits for a button press
//readyDelay(); //ready state is just a delay
if(spiroMode == true){
print(“Spiro mode true, swipes: “);
playDown(); //after user pushes button start soundtrack
//println(swipes);
//run the number of swipes input by user
for(int s = swipes; s > 0; s–){
print(“swipes: “);
println(s);
switchColor(); //swap color of marker before executing moves
moveExec(); //execute the moves storred in the movesArray
//after each swhipe (except the last) repeat the moves
if(s != 1){
readyState(); //ready state waits for a button press
noColor(); //does not draw before turning
robotSwipe(); //behavior to do after each swipe (turn right)
}
}
pauseMusic(); //after all swipes are done, pause the music
finishedYay();
}else{
println(“inside draw path else statement”);
playYoshi(); //play soundTrack
moveExec(); //execute the moves storred in the movesArray
finishedYay();
}
}
printPath();
break;
*/
case 0:
// Swap the color
switchColor();
break;
case 1:
// Move Spirobot Backward
if(pathMode == true){
//increment move counter
currMove++;
// Add move to display on GUI
addMoveMidMssg(‘2’);
// Add move to array of moves
addMove(‘2’);
//robotBkwd(moveDel);
//lServoBack();
//rServoBack();
}else if(spiroMode == true){
//increment move counter
currMove++;
// Add move to display on GUI
addMoveMidMssg(‘2’);
// Add move to array of moves
addMove(‘2’);
}else if(demoMode == true){
//pauseMusic();
//playR2();
//rewindR2();
//immediately move robot backward
robotBkwd(moveDel);
}
break;
case 2:
// Turn Spirobot left
if(pathMode == true){
//increment move counter
currMove++;
// Add move to display on GUI
addMoveMidMssg(‘4’);
// Add move to array of moves
addMove(‘4’);
//robotLeft(moveDel);
}else if(spiroMode == true){
//increment move counter
currMove++;
// Add move to display on GUI
addMoveMidMssg(‘4’);
// Add move to array of moves
addMove(‘4’);
}else if(demoMode == true){
//pauseMusic();
//playR2();
//rewindR2();
//immediately move robot left
rotateLeft(turnDel);
}
break;
case 3:
// Stop Spirobot
if(pathMode == true){
robotStop();
//lServoStop();
//rServoStop();
}else if(spiroMode == true){
robotStop();
}
else if(demoMode == true){
//pauseMusic();
//playR2();
//rewindR2();
//immediately stop robot
robotStop();
}
break;
case 4:
// Turn Spirobot right
if(pathMode == true){
//increment move counter
currMove++;
// Add move to display on GUI
addMoveMidMssg(‘6’);
// Add move to array of moves
addMove(‘6’);
//robotRight(moveDel);
}else if(spiroMode == true){
//increment move counter
currMove++;
// Add move to display on GUI
addMoveMidMssg(‘6’);
// Add move to array of moves
addMove(‘6’);
}else if(demoMode == true){
//pauseMusic();
//playR2();
//rewindR2();
//immediately move robot right
rotateRight(turnDel);
}
break;
case 5:
// Move Spirobot Forward
if(pathMode == true){
//increment move counter
currMove++;
// Add move to display on GUI
addMoveMidMssg(‘8’);
// Add move to array of moves
addMove(‘8’);
//robotFwd(moveDel);
//lServoFwd();
//rServoFwd();
}else if(spiroMode == true){
//increment move counter
currMove++;
// Add move to display on GUI
addMoveMidMssg(‘8’);
// Add move to array of moves
addMove(‘8’);
}else if(demoMode == true){
//pauseMusic();
//playR2();
//rewindR2();
//immediately move robot forward
robotFwd(moveDel);
}
break;
default:
//print debugging message
Serial.println(“reached default statement in commandExec(char com)”);
break;
}
}
/* switchColor()
*
* – switches between markers when called upon
*/
void switchColor(){
if(swapColor == true){
//switch to left
markerServo.write(0);
swapColor = false; //swap flag
}else if(swapColor ==false){
//switch to right
markerServo.write(175);
swapColor = true; //swap flag
}
}
/* noColor()
*
* – centers the servo so the robot is not actively drawing
*/
void noColor(){
markerServo.write(90);
}
/* finishedYay()
*
* – spirobot is finished drawing
* – play finished sounds
* – do little dance
*/
void finishedYay(){
//set booleans for end dance
boolean back = true;
boolean spin = true;
noColor();
delayT(2500); //pause to indicate finished
//playApplause(); //you deserve some applause!
//execute dance ints are in milsecs (1,000 ms = 1 sec)
if(back){
robotBkwd(2000); //move back for 3 seconds
back = false;
}
if(back == false && spin == true){
rotateLeft(5000);
}
}
/*****************
* DATA HANDLING *
******************/
/* addMove(char)
*
* – Adds move to the moveArray
* – The two modes, pathMode and spiroMode have bee split for future possible expansion
*/
void addMove(char move){
//Path mode and up to 10 moves
if(pathMode == true && currMove < 11){
//add the move to the movesArray
movesArray[currMove-1] = move;
}else if(spiroMode == true && currMove < 6){
//Spirograph mode and up to 5 moves
//add the move to the movesArray
movesArray[currMove-1] = move;
}
}
/* printPath()
*
* – Prints the moveArray to the command line
* – Primarily for testing purposes
*/
void printPath(){
int cnt = 0;
Serial.print(“moves: “);
while(movesArray[cnt] != ‘x’){
//print(“(pos: “);
//print(cnt);
//print(” )”);
Serial.print(movesArray[cnt]);
Serial.print(” ,”);
cnt++;
}
Serial.println(” end of list”);
}
/* moveExec()
*
* – Executes the moves from the moveArray
*/
void moveExec(){
int cnt = 0;
//light LED indicating robot is in motion
greenLED(true);
//go through moves array until encounter end of array marker
while(movesArray[cnt] != ‘x’){
//execute the encountered move
switch(movesArray[cnt]){
case ‘2’:
//Move Back
robotBkwd(moveDel);
break;
case ‘4’:
//Move Left
rotateLeft(turnDel);
break;
case ‘6’:
//Move Right
rotateRight(turnDel);
break;
case ‘8’:
//Move Forward
robotFwd(moveDel);
break;
default:
//print debugging message
Serial.println(“reached default statment in moveExec()”);
break;
} // end case statement
//increment counter to next move
cnt++;
} //end while loop
//turn off LED indicating robot is not in motion
greenLED(false);
}
/*********************
* SPIROBOT CONTROLS *
**********************/
/*robotSwipe()
*
* – executes behaviors after each swipe in spirograph mode
* – turn right
* – turn is in millisecs (1,000 ms = 1 sec)
*/
void robotSwipe(){
//for now rotate right
rotateRight(3000);
}
/*robotFwd(int)
* -moves robot forward for ‘del’ secs
* -1,000 milliseconds in a second (delay() is in millisecs)
*/
void robotFwd(int del){
// set saved time for timing purposes
savedTime= millis();
//println(“inside robotFwd()”);
//print(“del = “);
//println(del);
//print(“savedTime = “);
//println(savedTime);
//current time minus the last saved time
passedTime = millis() – savedTime;
//print(“initial passedTime = “);
//println(passedTime);
//do while the diff between the current time and last saved time is less than the delay wanted
while(passedTime < del){
//update the time that has passed
passedTime = millis() – savedTime;
//print(“inside while loop. passedTime = “);
//println(passedTime);
//move robot forward
lServoFwd(1560);
rServoFwd(1460);
}
Serial.println(“end of robotFwd(int)”); //testing purposes
// Save the current time to restart the timer
savedTime = millis();
//wait x seconds
//delayT(del);
//stop the robot
robotStop();
}
/*robotBkwd(int)
* -moves robot backward for ‘del’ secs
* -1,000 milliseconds in a second (del is in millisecs)
*/
void robotBkwd(int del){
// set saved time for timing purposes
savedTime= millis();
//current time minus the last saved time
passedTime = millis() – savedTime;
//do while the diff between the current time and last saved time is less than the delay wanted
while(passedTime < del){
//update the time that has passed
passedTime = millis() – savedTime;
//move robot forward
lServoBack(1460);
rServoBack(1560);
}
Serial.println(“end of robotBkwd(int)”); //testing purposes
// Save the current time to restart the timer
savedTime = millis();
//stop the robot
robotStop();
}
/*rotateLeft(int)
* -turns robot left for ‘del’ secs (enough to do a quarter turn)
* -1,000 milliseconds in a second (del is in millisecs)
*/
void rotateLeft(int del){
// set saved time for timing purposes
savedTime= millis();
//current time minus the last saved time
passedTime = millis() – savedTime;
//do while the diff between the current time and last saved time is less than the delay wanted
while(passedTime < del){
//update the time that has passed
passedTime = millis() – savedTime;
//move robot left
lServoBack(1460);
rServoFwd(1460);
}
Serial.println(“end of robotLeft(int)”); //testing purposes
// Save the current time to restart the timer
savedTime = millis();
//stop the robot
robotStop();
}
/*rotateRight(int)
* -turns robot right for ‘del’ secs (enough to do a quarter turn)
* -1,000 milliseconds in a second (del is in millisecs)
*/
void rotateRight(int del){
// set saved time for timing purposes
savedTime= millis();
//current time minus the last saved time
passedTime = millis() – savedTime;
//do while the diff between the current time and last saved time is less than the delay wanted
while(passedTime < del){
//update the time that has passed
passedTime = millis() – savedTime;
//move robot right
lServoFwd(1560);
rServoBack(1560);
}
Serial.println(“end of robotRight(int)”); //testing purposes
// Save the current time to restart the timer
savedTime = millis();
//stop the robot
robotStop();
}
/*robotStop()
* -stops both servos immediately
*/
void robotStop(){
lServoStop();
rServoStop();
}
/************************
* INDIV SERVO CONTROLS *
*************************/
/*lServFwd(int)
* -rotates servo “forward” (relative position)
* -1700 is max for forward motion (1560 was default)
*/
void lServoFwd(int val){
leftServo.write(val);
}
/*lServoBack(int)
* -rotates servo “backward” (relative position)
* -1300 is max for backward motion (1460 was default)
*/
void lServoBack(int val){
leftServo.write(val);
}
/*lServoStop()
* -stops left servo from rotating
*/
void lServoStop(){
leftServo.write(1510);
}
/*rServFwd(int)
* -rotates servo “forward” (relative position)
* -1300 is max for forward motion (1460was default)
*/
void rServoFwd(int val){
rightServo.write(val);
}
/*rServoBack(int)
* -rotates servo “backward” (relative position)
* -1700 is max for backward motion (1560 was default)
*/
void rServoBack(int val){
rightServo.write(val);
}
/*rServoStop()
* -stops left servo from rotating
* -1510 is stop (calibration from manuf.)
*/
void rServoStop(){
rightServo.write(1510);; // Stop
}
/**************************
* LED INDICATOR CONTROLS *
***************************/
/* greenLED(boolean)
*
* – on off switch for green LED
* – mostly used to indicate Spirobot is in motion
*/
void greenLED(boolean s){
//on off switch for green LED
if(s){
//turn on green LED
digitalWrite(13, HIGH);
}else{
//turn off green LED
digitalWrite(13, LOW);
}
}
/* yellowLED(boolean)
*
* – on off switch for yellow LED
* – mostly used to indicate Spirobot is in waiting
*/
void yellowLED(boolean s){
//on off switch for yellow LED
if(s){
//turn on yellow LED
digitalWrite(4, HIGH);
}else{
//turn off yellow LED
digitalWrite(4, LOW);
}
}
/* readyState()
*
* – ready state indicated by yellow LED on
* – spirobot waiting for button to be pushed to continue
* – basically a delay until button is pushed
*/
void readyState(){
boolean ready = true;
int state = LOW;
yellowLED(true);
Serial.println(“inside readyState() button push delay”);
//pauseMusic();
/*if(arduino.digitalRead(2) == Arduino.LOW){
println(“button is not pushed”);
}
if(arduino.digitalRead(2) == Arduino.HIGH){
println(“button is PUSHED!!!”);
}*/
while(ready){
//stop both servos from moving by default
leftServo.write(1515);
rightServo.write(1510);
//gives the arduino a second chance at reading the pin (more responsive)
state = digitalRead(2);
//gives the arduino a sort of delay so that it can read the pin again and
//not get caught in an endless loop. A sort of quirk if you will.
//Weird, it HAS to be a print statement. Maybe because of the Java Platform
//its running in the background.
Serial.print(” “);
//print(“inside while loop”);
if(digitalRead(2) == HIGH || state == HIGH){
ready = false;
Serial.println(“**** READY SHOULD BE FALSE ****”);
}
}
Serial.println(“out of while loop”);
blinkDelay(); //blinks light indicating button push accepted
readyDelay(); //delay allows user to back off before robot moves again
//turn off yello LED to indicate out of ready mode
yellowLED(false);
}
/* readyDelay()
*
* – ready state indicated by yellow LED on
* – after a delay of 2 secs (2000 value below) spirobot proceeds with movement
* – basically a delay of x seconds with yellow light indicator
*/
void readyDelay(){
//indicate in ready mode
yellowLED(true);
// set saved time for timing purposes
savedTime= millis();
//println(“inside robotFwd()”);
//print(“del = “);
//println(del);
//print(“savedTime = “);
//println(savedTime);
//current time minus the last saved time
passedTime = millis() – savedTime;
//print(“initial passedTime = “);
//println(passedTime);
//do while the diff between the current time and last saved time is less than the delay wanted
while(passedTime < 2000){
//update the time that has passed
passedTime = millis() – savedTime;
//print(“inside while loop. passedTime = “);
//println(passedTime);
}
Serial.println(“end of readyDelay())”); //testing purposes
// Save the current time to restart the timer
savedTime = millis();
//indicate end of ready mode
yellowLED(false);
}
/* blinkDelay()
*
* – blinks yellow LED 3 times
*/
void blinkDelay(){
//blink light 3 times
for(int i = 0; i < 3; i++){
yellowLED(false);
delayT(300); //allows light to flicker
yellowLED(true);
delayT(300); //allows light to flicker
}
}