HackingSTEM Joystick

HackingSTEM Joystick © MIT

Build a simple 3-axis joystick!

  • 7 views
  • 0 comments
  • 0 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
×1
Adafruit industries ada62 image 75px
USB-A to B Cable
×1
copper tape
×1
stranded wire, 11cm
×2
11026 02
Jumper wires (generic)
×7
826 04
Male/Female Jumper Wires
×7
paper plate
×1
wooden dowel, 6mm diameter, 12mm long
×1
cardboard!
×1
paper plate
×1
foam tape square
×1

Necessary tools and machines

Apps and online services

About this project

Build a joystick controller to pilot a digital shark as it surveys a virtual marine environment in Microsoft Excel. Use this project as-is or modify the joystick code to control your keyboard and/or mouse!

Download the Joystick Workbook here: https://onedrive.live.com/?authkey=%21AI5IcSCS8O%5FUMd0&id=D1EAAFC0BDFA320E%219640&cid=D1EAAFC0BDFA320E

Note: This tutorial is a shortened version of our original instructions, which you can find here: https://aka.ms/shark-lesson

Prepare Joystick Parts

Label the cardboard pieces: Plate Support (9x 25cm), Base (9x9cm), Handle 1 (5x5cm), Handle 2 (5x5cm), Yaw 1 (1x3cm), and Yaw 2 (1x3cm).

Assemble the Handle

Assemble the Base

Assemble the Plate Support

Assemble the Controller

Prepare Wires

Wire it up!

Flash your Arduino board, open the Excel workbook, and go play!

More to Explore

We love it when folks hack our projects! Use your joystick in the Excel workbook or modify the Arduino code to control a Human Interface Device ("HID") like your computer keyboard or mouse.

Let us know what you and/or your students create via social media, Linked In, or Flip Grid. Find us by searching for Microsoft HackingSTEM.

Other questions, comments, or ideas? Please feel free to reach out to us on our website: aka.ms/hackingSTEM

Happy making!

Code

BBC Joystick Arduino CodeArduino
This project relies upon the construction of a simple joystick controller illustrating pitch, roll, and yaw. For each axis there are 2 switches. All switches are set HIGH and there is a common GND. When a switch is activated the switch goes LOW. For each pair of switches the code emits 1, 0, or -1 indicating direction.
/*
 * 

  This code works with the How Do Sharks Swim workbook and lesson plan
  available from the Microsoft Education Workshop at http://aka.ms/hackingSTEM   
   
  This projects uses an Arduino UNO microcontroller board. More information can
  be found by visiting the Arduino website: https://www.arduino.cc/en/main/arduinoBoardUno 
  See https://www.arduino.cc/en/Guide/HomePage for board specific details and tutorials.

  This project relies upon the construction of a simple joystick controller illuststrating pitch, roll, and yaw. 
  For each axis there are 2 switches. All switches are set HIGH and there is a common GND. When a switch is
  activated the switch goes LOW. For each pair of switches the code emits 1, 0, or -1 indicating direction. 

  David Myka, 2018 Microsoft Education Workshop
  For issues with this code visit: https:aka.ms/hackingstemsupport
  
 *
 */

#include <Servo.h>

Servo pitch_servo;  // create servo object for pitch
Servo yaw_servo;  // create servo object yaw
Servo roll_servo;  // create servo object roll

//servo positions 0 to 180 (90 in the middle)

float pitch_pos = 90;
float yaw_pos = 90;
float roll_pos = 90;
const float POSITION_STEP = .015; //smaller steps means slower movement
const float PITCH_MAX = 140; //limits pitch, value 0 to 180 
const float PITCH_MIN = 25; //limits pitch, value 0 to 180

// Variables to track the 3 axis
int pitch; 
int yaw; 
int roll;

// SWITCH STATES
// x-axis
int roll_ccw; 
int roll_cw;

// y-axis
int pitch_ccw; 
int pitch_cw;

// z-axis
int yaw_ccw;
int yaw_cw; 
//-------------

// Game variables from Excel
String X;
String Y;
String Z;
String hitTime;
int reset = 0;
//------------------

// This should be 6 for a 3 axis joystick
const int numberOfInputPins = 6;




/*
 * -------------------------------------------------------------------------------------------------------
 * SERIAL DATA VARIABLES----------------------------------------------------------------------------------
 * -------------------------------------------------------------------------------------------------------
 */

 //Incoming Serial Data Array
const byte numberOfChannelsFromExcel = 6;  // ******>>>This must be equal to or greater then the number of channels set in Data Streamer Settings worksheet
String incomingSerialData[numberOfChannelsFromExcel];

const String DELIMITER = ",";           // Cordoba expects a comma delimeted string of data
char DOUBLEQUOTES =   char(34);         // Any fields containing commas are escaped with double quotes
String inputString = "";                // String variable to hold incoming data
boolean stringComplete = false;         // Variable to indicate the string is complete (newline found)
const int serialInterval = 60;          // Interval between serial writes
unsigned long serialPreviousTime;       // Timestamp to track interval



/*
 * ARDUINO INITIALIZATION CODE-----------------------------------------------------
 */

void setup() {
  Serial.begin(9600);  
  

  // Set all pins up to numberOfPins HIGH starting with startPin
  int startPin = 2;
  for(int i=0; i < numberOfInputPins; i++)
  {
    pinMode((i + startPin), INPUT);
    digitalWrite((i + startPin), HIGH);
  }

  yaw_servo.attach(9);   // attaches the yaw servo on pin 9 to the servo object
  pitch_servo.attach(10);  // attaches the pitch servo on pin 10 to the servo object
  roll_servo.attach(11);  // attaches the roll servo on pin 11 to the servo object

  pitch_servo.write(pitch_pos); //initialize servo position 
  yaw_servo.write(yaw_pos);     //initialize servo position 
  roll_servo.write(roll_pos);   //initialize servo position 

}

/*
 * START OF MAIN LOOP -------------------------------------------------------------
 */ 
void loop()
{
  // Process sensors
  processSensors();
 
  // Read Excel commands from serial port
  processIncomingSerial();

  // Test for reset trigger 
  if (reset==1)
  {
    resetGame();    // if we have a trigger then reset the game variables
  }

  // Process and send data to Excel via serial port
  processOutgoingSerial();  
}

/*
 * RESET GAME CODE-----------------------------------------------------------------------------------
 */

void resetGame()
{
  // Reset state variables for shark game in Excel
  X = "";
  Y = "0";
  Z = "-10.5";
  hitTime = "0";
}

/*
 * SENSOR INOUT CODE-----------------------------------------------------------------------------------
 */
void processSensors()
{
  // Read joystick poistions from digital pins
  // x-axis
  roll_ccw   = digitalRead(2);
  roll_cw    = digitalRead(3);

  // y-axis
  pitch_ccw  = digitalRead(4);
  pitch_cw   = digitalRead(5);  

  //z-axis
  yaw_ccw    = digitalRead(6); 
  yaw_cw     = digitalRead(7);  

  //PROCESS SWITCHES
  //roll----------------------------------
  if(roll_ccw==LOW)
  {
    DecrementRollPosition();
    roll = 1;
  } 
  else if(roll_cw==LOW)
  {
    IncrementRollPosition();
    roll = -1;
  }
  else
  {
    CenterRollPosition();
    roll = 0;
  }
  
  //pitch----------------------------------
  if(pitch_ccw==LOW)
  {
    IncrementPitchPosition();
    pitch = 1;
  } 
  else if(pitch_cw==LOW)
  {
    DecrementPitchPosition();
   
    pitch = -1;
  }
  else
  {
    CenterPitchPosition();
    pitch = 0;
  }

  //yaw----------------------------------
  if(yaw_ccw==LOW)
  {
    IncrementYawPosition();
    yaw = 1;
  } 
  else if(yaw_cw==LOW)
  {
    DecrementYawPosition();
    yaw = -1;
  }
  else
  {
    CenterYawPosition();
    yaw = 0;
  }
}


/*
 * INCOMING SERIAL DATA PROCESSING CODE-------------------------------------------------------------------
 */

void processIncomingSerial()
{
  getSerialData();
  parseSerialData();
}

/*
 * getSerialData()
 * 
 * Gathers bits from serial port to build inputString
 */
void getSerialData()
{
  while (Serial.available()) {
    char inChar = (char)Serial.read();    // get new char
    inputString += inChar;                // add it to input string
    if (inChar == '\n') {                 // if we get a newline... 
      stringComplete = true;              // we have a complete string of data to process
    }
  }
}

/*
 * parseSerialData() 
 * 
 * Parse all program control variables and data from Excel  
 */
void parseSerialData()
{
  if (stringComplete) { // process data from inputString to set program variables. 
    
    //Build an array of values from comma delimited serial string from Data Streamer
    BuildDataArray(inputString);

    //Set values based on array index - Data Out column A5 = 0, B5 = 1, C5 = 2, etc.
    X   = incomingSerialData[0];    // Data Out column A5
    Y   = incomingSerialData[1];    // Data Out column B5
    Z   = incomingSerialData[2];    // Data Out column C5
    hitTime = incomingSerialData[3];    // Data Out column D5
    reset   = incomingSerialData[4].toInt();    // Data Out column E5

    inputString = "";                         // reset inputString
    stringComplete = false;                   // reset stringComplete flag
  }
}


// Increases pitch position variable by one and send new position to servo
void IncrementPitchPosition() {
  if (pitch_pos < PITCH_MAX) {
    pitch_pos = pitch_pos + POSITION_STEP;
  }
  pitch_servo.write(pitch_pos);
}

void DecrementPitchPosition() {
  if (pitch_pos > PITCH_MIN) {
    pitch_pos = pitch_pos - POSITION_STEP;
  }
  pitch_servo.write(pitch_pos);
}

void CenterPitchPosition() {
  if (pitch_pos > 90) {
       pitch_pos = pitch_pos - POSITION_STEP;
  } else if (pitch_pos < 90) {
      pitch_pos = pitch_pos + POSITION_STEP;
  }  
  pitch_servo.write(pitch_pos);
}


void IncrementRollPosition() {
  if (roll_pos < 180) {
    roll_pos = roll_pos + POSITION_STEP;
  }
  roll_servo.write(roll_pos);
}

void DecrementRollPosition() {
  if (roll_pos > 0) {
    roll_pos = roll_pos - POSITION_STEP;
  }
  roll_servo.write(roll_pos);
}

void CenterRollPosition() {
    if (roll_pos > 90) {
       roll_pos = roll_pos - POSITION_STEP;
  } else if (roll_pos < 90) {
      roll_pos = roll_pos + POSITION_STEP;
  }  
  roll_servo.write(roll_pos);
}


void IncrementYawPosition() {
  if (yaw_pos < 180) {
    yaw_pos = yaw_pos + POSITION_STEP;
  }
  yaw_servo.write(yaw_pos);
}

void DecrementYawPosition() {
  if (yaw_pos > 0) {
    yaw_pos = yaw_pos - POSITION_STEP;
  }
  yaw_servo.write(yaw_pos);
}

void CenterYawPosition() {
    if (yaw_pos > 90) {
       yaw_pos = yaw_pos - POSITION_STEP;
  } else if (yaw_pos < 90) {
      yaw_pos = yaw_pos + POSITION_STEP;
  }  
  yaw_servo.write(yaw_pos);
}





/*
 * BuildDataArray()
 * 
 * Takes the comma delimited string from Data Streamer and splits the fields into an array
 * Follows CSV spec and encodes fields as follows: 
 *    Fields containing commas are wrapped in doublequotes. This allows for comma decimal separators in field values.
 *    Doublequotes in fields are escaped with a second doublequote.
 */
 
void BuildDataArray(String data)
{
  return ParseLine(data);
}

/*
 * ParseLine - parses a single string of comma delimited values terminated by a line ending character
 */
void ParseLine(String data) 
{
    int i = 0;    // tracks the character we are looking at in the data from Excel (data)
    int arrayIndex = 0;

    // For each index array that we are expecting from Excel
    while(arrayIndex < numberOfChannelsFromExcel)
    {
        String field = ParseNextField(data, i);   // parse the next field of data
        incomingSerialData[arrayIndex] = field;   // add field to incomingSerialDataArray
        arrayIndex++;   // increment index
    }
}

/*
 * ParseNextField - parses the next value field in between the comma delimiters
 */
String ParseNextField(String data, int &i)
{
    if (i >= data.length() )    // if character length is greater than data string length
    {
      return ""; //end of data
    }
    
    String field = "";
    bool hitDelimiter = false;    // flag for delimiter detection 
    while (hitDelimiter == false)   // loop through characters until we hit a delimiter
    {
        if (i >= data.length() )    // if character length is greater than data string length
        {
          break; //end of data
        }

        if (String(data[i]) == "\n")    // if character is a line break
        {
          break; // end of data
        }
        
       if(String(data[i]) == DELIMITER)   // if we hit a delimiter
        {
          hitDelimiter = true;  // flag the delimiter hit
          i++; // set iterator after delimiter so we skip the comma
          break;
        }
        else
        {        
          field += data[i];   // add character to field string
          i++;    // increment to next character in data
        }
    }
    return field;
}

/*
 * OUTGOING SERIAL DATA PROCESSING CODE-------------------------------------------------------------------
 */
void processOutgoingSerial()
{
  if((millis() - serialPreviousTime) > serialInterval)  // Enter into this only when interval has elapsed
  {
    serialPreviousTime = millis(); // Reset interval timestamp
    sendDataToSerial(); 
  }
}

void sendDataToSerial()
{
  Serial.print(roll);
 
  Serial.print(DELIMITER);
  Serial.print(pitch);
   
  Serial.print(DELIMITER);
  Serial.print(yaw);
  
  // Excel passthrough variables
  Serial.print(DELIMITER);
  Serial.print(X);

  Serial.print(DELIMITER);
  Serial.print(Y);
  
  Serial.print(DELIMITER);
  Serial.print(Z);
  
  Serial.print(DELIMITER);
  Serial.print(hitTime);
  
  Serial.println(); // Line ending character
}

Schematics

Arduino Joystick Schematic
Complete Arduino Schematic for the HackingSTEM joystick.
Uploads2ftmp2fc1413d9a 3bac 474a 9ed7 358da973c7792fyaw lqhqi8qixv

Comments

Similar projects you might like

Hacking STEM Electroconductivity Meter

by Jen Fox and Hacking STEM

  • 2,226 views
  • 4 comments
  • 17 respects

Joystick Game

Project tutorial by xXarduino_11Xx

  • 3,910 views
  • 5 comments
  • 13 respects

Hacking STEM Heat Shield Simulation

by Jen Fox and Hacking STEM

  • 765 views
  • 0 comments
  • 4 respects

Arduino Marble Maze Labyrinth

Project tutorial by AhmedAzouz

  • 9,534 views
  • 14 comments
  • 39 respects

Hacking STEM Flex/Pressure Sensor

Project tutorial by Jen Fox and Hacking STEM

  • 2,363 views
  • 0 comments
  • 8 respects

Arduino Joystick Servo Control Using 1Sheeld

Project tutorial by amrmostaafaa

  • 2,339 views
  • 0 comments
  • 12 respects
Add projectSign up / Login