Table of Contents



The Pennsylvania State UniversityDepartment of Aerospace EngineeringAERSP 440 - Introduction to Software EngineeringWhite Team – Coding Group Coding Group Report 5 April 2014Table of Contents TOC \o "1-3" \h \z \u Table of Contents PAGEREF _Toc258339042 \h IList of Figures PAGEREF _Toc258339043 \h IIList of Tables PAGEREF _Toc258339044 \h II1.0Software Design Overview PAGEREF _Toc258339045 \h 11.1Software Design Objective PAGEREF _Toc258339046 \h 11.2Software Specification PAGEREF _Toc258339047 \h 12.0User Manual PAGEREF _Toc258339048 \h 22.1Physical Components and Controls PAGEREF _Toc258339049 \h 22.2Preparing Robot and Computer PAGEREF _Toc258339050 \h 22.3Robot Movement Control PAGEREF _Toc258339051 \h 33.0Flowcharts PAGEREF _Toc258339052 \h 53.1Sequence Diagram PAGEREF _Toc258339053 \h 53.2State Diagram PAGEREF _Toc258339054 \h 63.3Activity Diagram PAGEREF _Toc258339055 \h 74.0Source Code PAGEREF _Toc258339056 \h 74.1Arduino Code PAGEREF _Toc258339057 \h 84.2PS3Interface.cpp PAGEREF _Toc258339058 \h 144.3PS3Controller.cpp PAGEREF _Toc258339059 \h 154.4PS3Controller.h PAGEREF _Toc258339060 \h 204.5UDPCon.cpp PAGEREF _Toc258339061 \h 234.6UDPCon.h PAGEREF _Toc258339062 \h 245.0Code Documentation PAGEREF _Toc258339063 \h 256.0Testing Guidelines PAGEREF _Toc258339064 \h 336.1Endurance Test PAGEREF _Toc258339065 \h 336.2Range Test PAGEREF _Toc258339066 \h 336.3Power Test PAGEREF _Toc258339067 \h 336.4Reset Test PAGEREF _Toc258339068 \h 346.5IR Sensor Test PAGEREF _Toc258339069 \h 347.0Programming Schedule PAGEREF _Toc258339070 \h 357.1Coding Team Schedule PAGEREF _Toc258339071 \h 357.2Hours Worked PAGEREF _Toc258339072 \h 368.0Cost Analysis PAGEREF _Toc258339073 \h 388.1Total Cost PAGEREF _Toc258339074 \h 38List of Figures TOC \h \z \c "Figure" Figure 31: Sequence Diagram of Robot Control System PAGEREF _Toc258339029 \h 5Figure 32: State Diagram of the Arduino program PAGEREF _Toc258339030 \h 6Figure 33: Activity Diagram of the Arduino program PAGEREF _Toc258339031 \h 7Figure 71: Gantt Chart Showing Coding Group Schedule PAGEREF _Toc258339032 \h 35Figure 72: Weekly Estimated and Actual Hours Worked PAGEREF _Toc258339033 \h 36Figure 73: Total Estimated and Actual Hours Worked PAGEREF _Toc258339034 \h 37Figure 81: Hours worked by members of the coding team as applied to a cost analysis. PAGEREF _Toc258339035 \h 38List of Tables TOC \h \z \c "Table" Table 21: Components and Controls for the Robot and Controller PAGEREF _Toc258339023 \h 2Table 22: Table of Controls and Reactions for Robot Movement Control PAGEREF _Toc258339024 \h 3Software Design OverviewAs the White Team Coding Group, we were responsible for developing the necessary software used for controlling a remotely operated vehicle with IR LED firing system. The software must not only control the vehicle, but meet the requirements set by the requirements team and follow the design developed by the design team. Software Design ObjectiveThe objective of the coding group was to develop working, meaningful software in accordance to the requirements and design specified. Not only did the software have to meet all of the requirements, it had to do so in a way that had minimal complexity, anticipated change, was designed for testing, and was above all else, reliable. The coding team analyzed the requirements documents and developed the code in a way that met all of the requirements listed all while keeping reliability in mind. Software SpecificationThe software developed covers the three main functional components of the system; drivers for the PS3 controller, Wi-Fi data transfer and packet transmission, and movement control for the robot itself.As specified by the requirements, the code written for the PS3 controller and wireless data transfer were written in C++ and the robot control program for the Arduino was written in the C++ variant compatible with the Arduino Uno.The code for the PS3 controller initializes the controller configuration, specifies the joystick axes, and converts the joystick output to values that are compatible with the Arduino processor. This code also contains a while loop that takes input from the controller continuously as long as the code is running. The body of this loop updates the input state of the controller and transmits the input data to the robot for actuation of the motor controls and IR LED. The data transfer code utilizes User Datagram Protocol (UDP) to transmit packets to the robot. UDP was chosen because it has the ability to drop packets instead of waiting for delayed packets to be read. This is the ideal protocol for real-time embedded systems because it prevents issues that would occur should packets be delayed. The two wheels on the robot are each individually controlled by left and right joystick respectively. The direction of each wheel is determined from the forward and aft movement of the joystick, and the speed of each is determined by the angle of the respective joystick. This code also specifies a dead zone to eliminate movement due to slight offsets that might exist in the joystick when releasing it to the neutral position. The IR laser is actuated by the right trigger button on the controller, and contains a built-in delay of two seconds between firings. User ManualThe following subsections describe the physical components, controls, how to prepare the robot and finally how to control the robot.Physical Components and ControlsTable STYLEREF 1 \s 2 SEQ Table \* ARABIC \s 1 1: Components and Controls for the Robot and ControllerRobotController Preparing Robot and ComputerEnsure the Arduino code is uploaded to the robot.Turn on the robot.Turn on the wireless router.Plug the controller into the computer via a USB port.Open the program “Better DS3.” This program is an emulator for a PS3 controller.Select the profile called “Master” from drop down menu within the Better DS3 interface.Open Microsoft Visual Studio and run the program “PS3 Interface”. This will allow the PS3 controller commands to be transmitted to the robot.Open the “IP Camera” program and double click on the IP address assigned to the camera.Enter the credentials: Username – “admin” and Password – [Leave Blank]Choose the proper mode that is compatible with your browser. ( e.g. “Server Push Mode” for Google Chrome and Safari )Wait a few seconds for the connection to establish and the video feed should appear on screen.After waiting a few seconds for any connection delays the computer and robot should now be ready to be used.Robot Movement ControlThe setup of the PS3 controller allows for maximum maneuverability of the robot. Each joystick on the controller controls one wheel so that the robot is capable of going only forward, only backward, only turning, turning while going forward, and also turning while going backwards. REF _Ref384127793 \h Table 22 describes different scenarios of joystick inputs and the corresponding movement of the robot.Table STYLEREF 1 \s 2 SEQ Table \* ARABIC \s 1 2: Table of Controls and Reactions for Robot Movement ControlJoystick InputRobot ReactionBoth Joysticks ForwardBoth Joysticks BackwardJoystick InputRobot ReactionLeft Joystick Completely ForwardRight Joystick Completely BackwardLeft Joystick Completely BackwardRight Joystick Completely ForwardLeft Joystick Partially ForwardRight Joystick Completely ForwardLeft Joystick Completely ForwardRight Joystick Partially ForwardFlowchartsThe following subsections detail the code written for the robot through various diagrams including sequence, state and activity diagrams.Sequence DiagramFigure STYLEREF 1 \s 3 SEQ Figure \* ARABIC \s 1 1: Sequence Diagram of Robot Control SystemState DiagramFigure STYLEREF 1 \s 3 SEQ Figure \* ARABIC \s 1 2: State Diagram of the Arduino programActivity DiagramFigure STYLEREF 1 \s 3 SEQ Figure \* ARABIC \s 1 3: Activity Diagram of the Arduino programSource CodeThe following subsections display the source code for each piece of the robot, computer and controller system. There is the Arduino Code for the robot, the connection codes for the controller and the computer, and the message passing code. It is important to note that the following programs with the exception of the Arduino Code were created following the how-to guide for wireless connections from the Georgia Tech Research Institute and modified by the White Team Coding Group.Arduino CodeThis program is the one that is run on the Arduino Uno board. It takes care of connecting to the network and receiving data from the controlling computer. This program also takes care of all of the movement controls needed for the robot once the data is received and also handles the firing of the IR LED.// Code for a robot controlled by a PS3 controller using an Arduino Uno, WIFI shield, and motor shield// Necessary libraries#include <SPI.h>#include <WiFi.h>#include <WiFiUdp.h>#include <avr/io.h> // for interupts// WIFI variablesint status = WL_IDLE_STATUS;long rssi; // used for wifi signal strength//White team routerchar ssid[] = "AERSP440White";char password[] = "superman"; unsigned int localPort = 2390; // local port to listen onchar packetBuffer[255]; // buffer to hold incoming packetsWiFiUDP Udp;// Motor Control variables// Motor Aconst int PWMA = 3; // motor A is on pin 3const int brakeA = 6; // motor A brake on pin 6 not 9const int dirA = A4; // motor A direction pin is on pin A4 not 12// Motor Bconst int PWMB = 5; // motor B is on pin 5 not 11const int brakeB = 8; // motor B brake on pin 8const int dirB = A5; // ,motor B direction pin is on pin A5 not 13// Firing pinconst int firePin = A3;// Sensor pinsconst int sensorPin = 2; // pin the IR sensor is onconst int LEDPin = A2; // LED on this pin will light up if robot is hitint lX, lY, rX, rY; // used for joystick commands and firing, rY is unusedvolatile int count = 0; // used to count time between firingvolatile int hitDetect = 0; // used to detect how long other team hits our robotvolatile boolean flagHit = false;void setup( ) { Serial.begin(9600); // used to begin serial comunication when connected to computer via USB cable // Setup pinModes // Setup Channel A pinMode(PWMA, OUTPUT); pinMode(dirA, OUTPUT); // initiates motor channel A pin pinMode(brakeA, OUTPUT); // initiates brake channel A pin // Setup Channel B pinMode(PWMB, OUTPUT); pinMode(dirB, OUTPUT); // initiates motor channel B pin pinMode(brakeB, OUTPUT); // initiates brake channel B pin // Setup fire pin pinMode(firePin, OUTPUT); // initiates firing pin // Setup sensor pins pinMode(sensorPin, INPUT); // Initiate the interups, written in assembly language cli(); // turn interups off OCR1A = 0x4E1F; TCCR1B |= (1 << WGM12); // Mode 4, CTC on OCR1A TCCR1B |= (1 << CS11); // set prescaler to 1024 and start the timer TIMSK1 |= (1 << OCIE1A); //Set interrupt on compare match sei(); // turn interups on // Interup polls sensorPin every 50 milliseconds // Check for wifi shield while(WiFi.status() == WL_NO_SHIELD) { Serial.println("No Wifi shield present"); delay(1000); // waits for 1 second before trying again } // Check current firmware version String fv = WiFi.firmwareVersion(); // unecessary now, used at begining to update firmware if ( fv != "1.1.0" ) { Serial.print("Please upgrade the firmware. Current version: "); Serial.println(fv); } else { Serial.print("Firmware version: "); Serial.println(fv); } wifiConnect(ssid, password, 1000); // fuunction created to connect to wifi} void loop( ) { int packetSize = Udp.parsePacket(); // receive packet if there is one if (packetSize) { // if there is a packet // read a packet into the buffer int length = Udp.read(packetBuffer,255); if(length > 0 ) { packetBuffer[length] = 0; } char *char_pointer; char_pointer = strtok(packetBuffer, " "); // string to token, deliminater is " " for( int i=0; i<4; i++) { if(i == 0) { lX = atoi(char_pointer); } // atoi is a char to an int. Takes in a pointer to a char if(i == 1) { lY = atoi(char_pointer); } if(i == 2) { rX = atoi(char_pointer); } if(i == 3){ rY = atoi(char_pointer); } char_pointer = strtok(NULL," "); } if( flagHit == true) { Serial.println("Hit"); digitalWrite(A2, HIGH); digitalWrite(brakeA, HIGH); digitalWrite(brakeB, HIGH); analogWrite(PWMA, 0); analogWrite(PWMB, 0); delay(2000); digitalWrite(A2, LOW); flagHit = false; hitDetect = 0; } moveRobot(lY,rY); // created function to move the robot if (count > 20) { fireLED(rX); // function to handle firing count = 0; }} rssi = WiFi.RSSI(); // get wifi signal if (rssi > 0) { // RSSI is 238 is no connection. Assume lost connection if RSSI > 230 // If connection lost, stop the robot digitalWrite(brakeA, HIGH); digitalWrite(brakeB, HIGH); analogWrite(PWMA, 0); analogWrite(PWMB, 0); Serial.println("Lost signal to router. Will try to reconnect."); wifiConnect(ssid, password, 5000); // Try to recconect to the wiFi }}ISR ( TIMER1_COMPA_vect ) { count++; if ( digitalRead( sensorPin ) ) { hitDetect = 0; } else { hitDetect+=1; } if(hitDetect >= 5){ flagHit = true; }}void wifiConnect(char ssid[], char password[], int time) { // Connect to wifi network while (status != WL_CONNECTED) { Serial.print("Attempting to connect to SSID: "); Serial.println(ssid); status = WiFi.begin(ssid,password); delay(time); // waits for 1 second before trying again } Serial.println("Connected to wifi."); IPAddress ip; ip = WiFi.localIP(); // get IP address of wifi shield Serial.println(ip); // print IP address Serial.println("Starting connection to server"); Udp.begin(localPort); // begin connection on specified port Serial.println("Connected");}void moveRobot( int lY, int rY) { // only need Y values for tank style turning. Wheels can not turn in 2D if ( lY < 120 ) { // left joystick pushed up digitalWrite (brakeA, LOW); // turns brake off digitalWrite(dirA, HIGH); // sets direction to FORWARD analogWrite(PWMA, (lY-120)*-1.45); // increases speed as joystick is moved up } if ( lY > 135 ) { // left joystick pushed down digitalWrite(brakeA, LOW); // turns brake off digitalWrite(dirA, LOW); // sets direction to REVERSE analogWrite(PWMA, (lY-135)*1.45); // increases speed as joystick is moved down } if ( rY < 120 ) { // right joystick pushed up digitalWrite(brakeB, LOW); // turns brake off digitalWrite(dirB, HIGH); // sets direction to FORWARD analogWrite(PWMB, (rY-120)*-1.7); // increases speed as joystick is moved up } if ( rY > 135 ) { // right joystick pushed down digitalWrite(brakeB, LOW); // turns brake off digitalWrite(dirB, LOW); // sets direction to REVERSE analogWrite(PWMB, (rY-135)*1.7); // increases speed as joystick is moved down } if (lY > 120 && lY < 135) { // left joystick resting in middle digitalWrite(brakeA, HIGH); // turn brake on analogWrite(PWMA, 0); // set speed to 0 } if (rY > 120 && rY < 135) { // right joystick resting in middle digitalWrite(brakeB, HIGH); // turn brake on analogWrite(PWMB, 0); // set speed to 0 }} void fireLED( int rX ) { if ( rX > 100 ) { // stop the motors or robot will execute last command during the half second delay Serial.println("Fire"); digitalWrite(firePin, HIGH); digitalWrite(brakeA, HIGH); digitalWrite(brakeB, HIGH); analogWrite(PWMA, 0); analogWrite(PWMB, 0); delay(500); // keeps IR LED on for half a second, no other commands can be input during this second digitalWrite(firePin, LOW); // turn off firing pin } else { digitalWrite(firePin, LOW); }}PS3Interface.cppThis program is the main program that is ran on the controlling computer. It initializes the controller and UDP connection. It also receives the input from the controller, scales the data so it is compatible with the Arduino, and then sends the data to the Arduino board over Wi-Fi. // Defines the entry point for the console application. This program can print values from a PS3 Dualshock 3 Controller to a command line, and send the values over wifi to another device#define STRICT#define DIRECTINPUT_VERSION 0x0800#define _CRT_SECURE_NO_DEPRECATE#ifndef _WIN32_DCOM#define _WIN32_DCOM#endif#include "stdafx.h"//include file for standard system include files, project specific include files#include <stdio.h> //C Standard Input and Output Library#include <stdlib.h> //general purpose functions including dynamic memory management, random number generation, environment communication, integer arithmetics, searching, sorting, converting#include <iostream> //header that defines standard input/output stream objects#include <iomanip> //required for multi column prints out to the command window#include "UDPCon.h"#include <windows.h> //contains functions related to programming in Windows, essentially the WinAPI#include <stdio.h> //input and output operations can be performed using stdio.h and streams to operate devices#include "PS3Controller.h" //header file with structures for controller status and function declarationusing namespace std; //allows for text output to command line//-----------------------------------------------------------------------------// MAIN PROGRAM//-----------------------------------------------------------------------------int _tmain(int argc, _TCHAR* argv[]){//ps3 initialize PS3Controller MyController;bool quit = false;//udp initializeUDPCon myConnection; myConnection.Initialize(); myConnection.ConfigRecieveAddr();//Loop//for( int controllerUpdate = 0; controllerUpdate < 50000 /*how long you would like to run the program*/; controllerUpdate++)while(!quit){ MyController.UpdateInputState();//Sending the datamyConnection.SendJoystickData(MyController.getControllerAnalogPosScaled(1), MyController.getControllerAnalogPosScaled(2), MyController.getControllerAnalogPosScaled(3), MyController.getControllerAnalogPosScaled(4));Sleep(100);}}PS3Controller.cppThis program initializes the controller. It then creates a controller object and configures the joysticks. It also defines the joystick axis, and then reads the state of the joysticks. This program also contains the function used to scale the joystick data so it is compatible with the Arduino. // Contains structures and functions essential to the execution of PS3Interface.cpp#include "StdAfx.h"#include "PS3Controller.h"PS3Controller::PS3Controller(void)//constructor{g_pDI = NULL; g_pJoystick = NULL;Initialize();}PS3Controller::~PS3Controller(void)//destructor{FreeDirectInput();UpdateInputState();}HRESULT PS3Controller::Initialize(){ HRESULT hr; // Register with the DirectInput subsystem and get a pointer // to a IDirectInput interface we can use. // Create a DInput object if( FAILED( hr = DirectInput8Create( GetModuleHandle( NULL ), DIRECTINPUT_VERSION, IID_IDirectInput8, ( VOID** )&g_pDI, NULL ) ) ) return hr;DIJOYCONFIG PreferredJoyCfg = {0}; DI_ENUM_CONTEXT enumContext; enumContext.pPreferredJoyCfg = &PreferredJoyCfg; enumContext.bPreferredJoyCfgValid = false;//////////////////////////////////////////////////////// IDirectInputJoyConfig8* pJoyConfig = NULL; if( FAILED( hr = g_pDI->QueryInterface( IID_IDirectInputJoyConfig8, ( void** )&pJoyConfig ) ) ) return hr; PreferredJoyCfg.dwSize = sizeof( PreferredJoyCfg ); if( SUCCEEDED( pJoyConfig->GetConfig( 0, &PreferredJoyCfg, DIJC_GUIDINSTANCE ) ))std::cout << "PS3 Controller Connected" << "\n";std::cout << "First value outputted is junk. Please ignore." << "\n"; // This function is expected to fail if no joystick is attached enumContext.bPreferredJoyCfgValid = true; SAFE_RELEASE( pJoyConfig );/////////////////////////////////////////////////////////// if( FAILED( hr = g_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL, &PS3Controller::enumJoysticksTrampoline,this ,DIEDFL_ATTACHEDONLY ) ) ) return hr; // Set the data format to "simple joystick" - a predefined data format // // A data format specifies which controls on a device we are interested in, // and how they should be reported. This tells DInput that we will be // passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState(). if( FAILED( hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick2 ) ) ) return hr; // Set the cooperative level to let DInput know how this device should // interact with the system and with other DInput applications. if( FAILED( hr = g_pJoystick->SetCooperativeLevel( ::GetDesktopWindow(), DISCL_BACKGROUND |DISCL_NONEXCLUSIVE ) ) ) return hr;return S_OK;}//-----------------------------------------------------------------------------// Name: EnumJoysticksCallback()// Desc: Called once for each enumerated joystick. If we find one, create a// device interface on it so we can play with it.//-----------------------------------------------------------------------------BOOL CALLBACK PS3Controller::EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext ){ DI_ENUM_CONTEXT* pEnumContext = ( DI_ENUM_CONTEXT* )pContext; HRESULT hr; //if( g_bFilterOutXinputDevices && IsXInputDevice( &pdidInstance->guidProduct ) ) // return DIENUM_CONTINUE; // Skip anything other than the perferred joystick device as defined by the control panel. // Instead you could store all the enumerated joysticks and let the user pick. if( pEnumContext->bPreferredJoyCfgValid && !IsEqualGUID( pdidInstance->guidInstance, pEnumContext->pPreferredJoyCfg->guidInstance ) ) return DIENUM_CONTINUE; // Obtain an interface to the enumerated joystick. hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystick, NULL ); // If it failed, then we can't use this joystick. (Maybe the user unplugged // it while we were in the middle of enumerating it.) if( FAILED( hr ) ) return DIENUM_CONTINUE; // Stop enumeration. Note: we're just taking the first joystick we get. You // could store all the enumerated joysticks and let the user pick. return DIENUM_STOP;}HRESULT PS3Controller::UpdateInputState( ){HRESULT hr;//data type for error/warning conditionsTCHAR strText[512] = {0}; // Device state textDIJOYSTATE2 js; // DInput joystick state if( NULL == g_pJoystick )return S_OK;// Poll the device to read the current statehr = g_pJoystick->Poll();if( FAILED( hr ) ){// DInput is telling us that the input stream has been// interrupted. We aren't tracking any state between polls, so// we don't have any special reset that needs to be done. We// just re-acquire and try again.hr = g_pJoystick->Acquire();while( hr == DIERR_INPUTLOST )hr = g_pJoystick->Acquire();// hr may be DIERR_OTHERAPPHASPRIO or other errors. This// may occur when the app is minimized or in the process of // switching, so just try again later //return S_OK;}// Get the input's device state if( FAILED( hr = g_pJoystick->GetDeviceState( sizeof( DIJOYSTATE2 ), &js ) ) ) return hr; // The device should have been acquired during the Poll()//Define direction_axis -> js.l(direction)(axis);myCS.left_y = js.lY;myCS.left_x = js.lX;myCS.right_y = js.lRy;myCS.right_x = js.lRx;//std::cout << myCS.left_y << " " << myCS.right_y << "\n";return S_OK;//Return success code}//-----------------------------------------------------------------------------// Name: FreeDirectInput()// Desc: Initialize the DirectInput variables.//-----------------------------------------------------------------------------VOID PS3Controller::FreeDirectInput(){ // Unacquire the device one last time just in case // the app tried to exit while the device is still acquired. if( g_pJoystick ) g_pJoystick->Unacquire(); // Release any DirectInput objects. SAFE_RELEASE( g_pJoystick ); SAFE_RELEASE( g_pDI );}int PS3Controller::getControllerAnalogPosScaled(int which)//determine which joystick input you would like{//number for each of the following //1 = lx 65535 is highest x, 2 = ly 32511, 3 = rx, 4 = ryswitch (which){std::cout << "here";case(1):return myCS.left_x/256;break;case(2)://std::cout << myCS.left_y << " " << myCS.left_y/256 << "\n";return myCS.left_y/256;break;case(3):return myCS.right_x/256;break;case (4)://std::cout << myCS.right_y << " " << myCS.right_y/256 << "\n";return myCS.right_y/256;break;}}int PS3Controller::scaleAnalogPos(int pos)//scale the value to work with the robot{if(pos= myCS.left_x){return pos/= 255;//take raw joystick value and convert to value compatible with robot}else{return pos/= 255;}}PS3Controller.hThis program creates the structure defining the variables used for the position of the joysticks.// Contains structures and classes used by PS3Controller.cpp and Ps3Interface.cpp#pragma once#define STRICT#define DIRECTINPUT_VERSION 0x0800#define _CRT_SECURE_NO_DEPRECATE#ifndef _WIN32_DCOM#define _WIN32_DCOM#endif#include "stdafx.h" //include file for standard system include files, project specific include files#include <stdio.h> //C Standard Input and Output Library#include <stdlib.h> //general purpose functions including dynamic memory management, random number generation, environment communication, integer arithmetics, searching, sorting, converting#include <iostream> //header that defines standard input/output stream objects#include <InitGuid.h> //System and driver specific header files that contain GUID definitions#include <windows.h> //contains functions related to programming in Windows, essentially the WinAPI#include <stdio.h> //input and output operations can be performed using stdio.h and streams to operate devices#include <tchar.h> //unicode mapping layer that can be used to adapt to unicode and non-unicode environments#include <commctrl.h> //functions and toolbars used to create and manage a toolbar#include <basetsd.h> //Used to define new data types and macros in 32 and 64 bit environments#pragma warning(push)#pragma warning(disable:6000 28251)#include <dinput.h>//part of the DirectX SDK#pragma warning(pop)#include <dinputd.h>//Direct input header file#include <assert.h>//defines a macro function that can be used as a standard debugging tool#include <oleauto.h>//allows for OLE Automation#include <shellapi.h>//Windows Shell API//-----------------------------------------------------------------------------// Defines, constants, and global variables//-----------------------------------------------------------------------------#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }//-----------------------------------------------------------------------------// STRUCTURES//-----------------------------------------------------------------------------struct DI_ENUM_CONTEXT{ DIJOYCONFIG* pPreferredJoyCfg; bool bPreferredJoyCfgValid;};struct controllerStatus{int left_y;int left_x;int right_x;int right_y;int pos; };class PS3Controller{public://VariablesLPDIRECTINPUT8 g_pDI;LPDIRECTINPUTDEVICE8 g_pJoystick;//FunctionsPS3Controller(void); ~PS3Controller(void);controllerStatus PS3Controller::myCS;int scaleAnalogPos(int pos); //take in 0-65535, spit out 0 - 255int getControllerAnalogPosScaled(int which); //1 = lx, 2 = ly, 3 = rx, 4 = ryHRESULT UpdateInputState( );HRESULT Initialize();VOID FreeDirectInput();//BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext );private://controllerStatus PS3Controller::myCS;static BOOL CALLBACK enumJoysticksTrampoline( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef ) { return reinterpret_cast<PS3Controller*>(pvRef) ->EnumJoysticksCallback(lpddi, pvRef); }};UDPCon.cppThis program first initializes the socket that the data is sent over. It then defines the port the Arduino is listening on and the IP address used by the Arduino board so it knows where to send the data. The joystick positions are then put into a packet and sent to the receiver on the IP address specified.#include "UDPCon.h"UDPCon::UDPCon(){ slen = sizeof(si_other) ; }UDPCon::~UDPCon(){ closesocket(s); WSACleanup();}void UDPCon::Initialize(){ //Initialise winsock printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); exit(EXIT_FAILURE); } printf("Initialised.\n"); //Create a socket if((s = socket(AF_INET , SOCK_DGRAM , 0 )) == INVALID_SOCKET) { printf("Could not create socket : %d" , WSAGetLastError()); } printf("Socket created.\n"); }void UDPCon::SendData(const char *SendBuf){ //now reply the client with the same data if (sendto(s, SendBuf, strlen(SendBuf), 0, (struct sockaddr*) &RecvAddr, sizeof(RecvAddr)) == SOCKET_ERROR) { printf("sendto() failed with error code : %d" , WSAGetLastError()); exit(EXIT_FAILURE); } }void UDPCon::SendJoystickData(int Lx, int Ly, int Rx, int Ry){ //Turn ints into one big char* std::stringstream sstm; //std::string str; sstm << Lx << " " << Ly << " " << Rx << " " << Ry << "\n"; std::string str = sstm.str(); std::cout << str << "\n"; const char *SendBuf = str.c_str(); //now reply the client with the same data SendData(SendBuf); }void UDPCon::ConfigRecieveAddr(){ //--------------------------------------------- // Set up the RecvAddr structure with the IP address of // the receiver (in this example case "192.168.1.1") // and the specified port number. RecvAddr.sin_family = AF_INET; RecvAddr.sin_port = htons(REMOTEPORT); RecvAddr.sin_addr.s_addr = inet_addr("192.168.1.122");}UDPCon.hThis program creates the class used for the UDP communication object.#include <cstdlib>#include <iostream>#include <sstream>#include<stdio.h>#include<winsock2.h>#pragma comment(lib,"ws2_32.lib") //Winsock Library #define BUFLEN 512 //Max length of buffer#define PORT 17070 //The port on which to listen for incoming data#define REMOTEPORT 2390 //The port of the client we will be sending messages toclass UDPCon{ public: UDPCon(); ~UDPCon(); public: SOCKET s; struct sockaddr_in server, si_other; struct sockaddr_in RecvAddr; int slen , recv_len; char buf[BUFLEN]; WSADATA wsa; void Initialize(); void SendData(const char *SendBuf); void ConfigRecieveAddr(); void SendJoystickData(int Lx, int Ly, int Rx, int Ry);};Code Documentation// Code for a robot controlled by a PS3 controller using an Arduino Uno, WIFI shield, and motor shield// Necessary libraries#include <SPI.h>#include <WiFi.h>#include <WiFiUdp.h>#include <avr/io.h> // for interruptsDefine the variables used for connecting to the Wi-Fi router.// WIFI variablesint status = WL_IDLE_STATUS;long rssi; // used for wifi signal strength//White team routerchar ssid[] = "AERSP440White";char password[] = "superman"; unsigned int localPort = 2390; // local port to listen onchar packetBuffer[255]; // buffer to hold incoming packetsWiFiUDP Udp;Define the motor control pins and the pin the IR LED goes on. Each motor has a PWM, direction, and brake pin. Also define pins the IR sensor and LED light are on.// Motor Control variables// Motor Aconst int PWMA = 3; // motor A is on pin 3const int brakeA = 6; // motor A brake on pin 6 not 9const int dirA = A4; // motor A direction pin is on pin A4 not 12// Motor Bconst int PWMB = 5; // motor B is on pin 5 not 11const int brakeB = 8; // motor B brake on pin 8const int dirB = A5; // ,motor B direction pin is on pin A5 not 13// Firing pinconst int firePin = A3;// Sensor pinsconst int sensorPin = 2; // pin the IR sensor is onconst int LEDPin = A2; // LED on this pin will light up if robot is hitlX, lY, rX, and rY are the variables that will be used to store the data from the controller once the wireless packet is parsed. Count is used to keep track of the time between firings and hitDetect is used to measure the amount of time our robot is hit.int lX, lY, rX, rY; // used for joystick commands and firing, rY is unusedvolatile int count = 0; // used to count time between firingvolatile int hitDetect = 0; // used to detect how long other team hits our robotvolatile boolean flagHit = false;The function setup() creates the Wi-Fi connection and assigns the required pins to their appropriate functions. void setup( ) {Start the serial port at baud rate of 9600 when the Arduino is connected to the computer via USB cable. Serial.begin(9600); // used to begin serial communication when connected to computer via USB cableSet up each of the pins the motors, IR LED, IR sensor, and LED are set on. // Setup pinModes // Setup Channel A pinMode(PWMA, OUTPUT); pinMode(dirA, OUTPUT); // initiates motor channel A pin pinMode(brakeA, OUTPUT); // initiates brake channel A pin // Setup Channel B pinMode(PWMB, OUTPUT); pinMode(dirB, OUTPUT); // initiates motor channel B pin pinMode(brakeB, OUTPUT); // initiates brake channel B pin // Setup fire pin pinMode(firePin, OUTPUT); // initiates firing pin // Setup sensor pins pinMode(sensorPin, INPUT); This turns off interrupts. It then sets an interrupt to check the pin the IR sensor is on every 50 milliseconds. It then turns interrupts back on. // Initiate the interups, written in assembly language cli(); // turn interups off OCR1A = 0x4E1F; TCCR1B |= (1 << WGM12); // Mode 4, CTC on OCR1A TCCR1B |= (1 << CS11); // set prescaler to 1024 and start the timer TIMSK1 |= (1 << OCIE1A); //Set interrupt on compare match sei(); // turn interups on // Interup polls sensorPin every 50 milliseconds Check for the presence of a Wi-Fi shield. It will not proceed until a Wi-Fi shield is found. // Check for wifi shield while(WiFi.status() == WL_NO_SHIELD) { Serial.println("No Wifi shield present"); delay(1000); // waits for 1 second before trying again }Check the firmware version. The firmware version needs to be version 1.1.0 to support the UDP message passing. If it is not, the firmware needs to be updated and a message indicating the update is displayed to the screen. If the version is correct the version number is printed to the screen and the code proceeds. // Check current firmware version String fv = WiFi.firmwareVersion(); // unecessary now, used at begining to update firmware if ( fv != "1.1.0" ) { Serial.print("Please upgrade the firmware. Current version: "); Serial.println(fv); } else { Serial.print("Firmware version: "); Serial.println(fv); }Calls the wifiConnect function defined below which connects to the specified network. wifiConnect(ssid, password, 1000); // fuunction created to connect to wifi} The function loop() creates the loop that will be iterated during program execution of the robot control. void loop( ) {Read a packet if one is received. int packetSize = Udp.parsePacket(); // receive packet if there is one if (packetSize) { // if there is a packetGet length of packet. Clear previous buffer if there is a new packet. // read a packet into the buffer int length = Udp.read(packetBuffer,255); if(length > 0 ) { packetBuffer[length] = 0; }Create a pointer to a character. The function strtok() splits a string into tokens and returns a pointer to the last token. A token is a set of characters separated by a delimiter. In this case the delimiter is a “space”. The pointer created is then set to point to the first token. char *char_pointer; char_pointer = strtok(packetBuffer, " "); // string to token, deliminater is " "The function atoi() takes a char and returns an integer value that is used to set the values of the joystick. for( int i=0; i<4; i++) { if(i == 0) { lX = atoi(char_pointer); } // atoi is a char to an int. Takes in a pointer to a char if(i == 1) { lY = atoi(char_pointer); } if(i == 2) { rX = atoi(char_pointer); } if(i == 3){ rY = atoi(char_pointer); } char_pointer = strtok(NULL," "); }If the IR sensor senses a hit, the robot will stop and turn on the LED for 2 seconds. The robot is then set back to un-hit and the amount of time the robot was hit is set back to zero. if( flagHit == true) { Serial.println("Hit"); digitalWrite(A2, HIGH); digitalWrite(brakeA, HIGH); digitalWrite(brakeB, HIGH); analogWrite(PWMA, 0); analogWrite(PWMB, 0); delay(2000); digitalWrite(A2, LOW); flagHit = false; hitDetect = 0; }Call the moveRobot and fireLED functions, which are specified below. The fireLED function will only be called if count is greater than 20. This corresponds to a delay of 2 seconds since the robot last fired. moveRobot(lY,rY); // created function to move the robot if (count > 20) { fireLED(rX); // function to handle firing count = 0; }}The function WiFi.RSSI() will get the signal strength of the wifi. A signal strength of -70 to -40 is typical and considered a good signal strength. When the router is disconnected, a signal strength of 238 is reported. If the signal strength becomes greater than 0, the connection is assumed lost. The motors are then given a command to stop and then the Arduino attempts to reconnect to the network. rssi = WiFi.RSSI(); // get wifi signal if (rssi > 0) { // RSSI is 238 is no connection. Assume lost connection if RSSI > 230 // If connection lost, stop the robot digitalWrite(brakeA, HIGH); digitalWrite(brakeB, HIGH); analogWrite(PWMA, 0); analogWrite(PWMB, 0); Serial.println("Lost signal to router. Will try to reconnect."); wifiConnect(ssid, password, 5000); // Try to recconect to the wiFi }}This checks the pin the IR sensor is on to see if the robot is getting hit. If it is not, hitDetect is set to 0. If the robot is getting hit, a counter is started to see how long it is getting hit. If the robot is hit for 5 milliseconds or more the robot is counted as hit. ISR ( TIMER1_COMPA_vect ) { count++; if ( digitalRead( sensorPin ) ) { hitDetect = 0; } else { hitDetect+=1; } if(hitDetect >= 5){ flagHit = true; }}Function to connect to the Wi-Fi. The code will not proceed until it is connected to a Wi-Fi device. void wifiConnect(char ssid[], char password[], int time) { // Connect to wifi network while (status != WL_CONNECTED) { Serial.print("Attempting to connect to SSID: "); Serial.println(ssid); status = WiFi.begin(ssid,password); delay(time); // waits for 1 second before trying again } Display a message to the screen indicating a successful connection to the Wi-Fi network. Display the LAN IP address assigned to the Wi-Fi Shield. The Wi-Fi Shield was assigned a static LAN IP address of 192.168.1.3 from the router. This will also begin the UPD process, which is the data sending protocol. Serial.println("Connected to wifi."); IPAddress ip; ip = WiFi.localIP(); // get IP address of wifi shield Serial.println(ip); // print IP address Serial.println("Starting connection to server"); Udp.begin(localPort); // begin connection on specified port Serial.println("Connected");}Because the wheels can only rotate and cannot pivot, only Y axis values are required to move the wheels. The joystick fully forward is set to 0, fully backward is set to 255, and at rest is set to 127. The function in the analogWrite() function pertaining to each motor is different to adjust for drift noticed during testing. Also, the full 5 volts cannot be applied to the motors because it will draw too much power and brown out the Wi-Fi shield. To stop, 120 to 135 is used, not just 127, because joysticks do not always move back to exactly 127.void moveRobot( int lY, int rY) { // only need Y values for tank style turning. Wheels can not turn in 2D if ( lY < 120 ) { // left joystick pushed up digitalWrite (brakeA, LOW); // turns brake off digitalWrite(dirA, HIGH); // sets direction to FORWARD analogWrite(PWMA, (lY-120)*-1.45); // increases speed as joystick is moved up } if ( lY > 135 ) { // left joystick pushed down digitalWrite(brakeA, LOW); // turns brake off digitalWrite(dirA, LOW); // sets direction to REVERSE analogWrite(PWMA, (lY-135)*1.45); // increases speed as joystick is moved down } if ( rY < 120 ) { // right joystick pushed up digitalWrite(brakeB, LOW); // turns brake off digitalWrite(dirB, HIGH); // sets direction to FORWARD analogWrite(PWMB, (rY-120)*-1.7); // increases speed as joystick is moved up } if ( rY > 135 ) { // right joystick pushed down digitalWrite(brakeB, LOW); // turns brake off digitalWrite(dirB, LOW); // sets direction to REVERSE analogWrite(PWMB, (rY-135)*1.7); // increases speed as joystick is moved down } if (lY > 120 && lY < 135) { // left joystick resting in middle digitalWrite(brakeA, HIGH); // turn brake on analogWrite(PWMA, 0); // set speed to 0 } if (rY > 120 && rY < 135) { // right joystick resting in middle digitalWrite(brakeB, HIGH); // turn brake on analogWrite(PWMB, 0); // set speed to 0 }}The function fireLED() is used to fire the IR LED. rX was not used to control the wheel motors so it was set to R2 on the PS3 controller and will fire the IR LED. Rest is set to 0 and engaged is set to 255. To avoid an accidental fire the controller button will not send the fire signal until it is depressed approximately 40% (The value needs to be greater than 100). This will also ensure that the fire signal will be sent given a full depress of the button. The LED will remain illuminated for half a second when fired to provide enough time for the receiver to register the hit. void fireLED( int rX ) { if ( rX > 100 ) { // stop the motors or robot will execute last command during the half second delay Serial.println("Fire"); digitalWrite(firePin, HIGH); digitalWrite(brakeA, HIGH); digitalWrite(brakeB, HIGH); analogWrite(PWMA, 0); analogWrite(PWMB, 0); delay(500); // keeps IR LED on for half a second, no other commands can be input during this second digitalWrite(firePin, LOW); // turn off firing pin } else { digitalWrite(firePin, LOW); }}Testing GuidelinesThe following subsections provide a basis and rough guideline for the testing team to follow when testing the robot.Endurance TestAn endurance test should be run to make sure the battery is capable of providing enough power to the robot and camera for the duration of the competition. If the battery is not capable, determine how long a battery will last before it needs to be replaced so performance is not effected.Range TestThe maximum range of the robot should be determined so it is never driven outside of the range of the router during the competition. Tests should also be run to see if there is any delay in camera feed or robot movement when the controller is in a different room than the router and robot.Power TestTests should also be run to determine the optimal power to the motors. If too much power is sent to the motors the Wi-Fi shield will stop working. However, if too little power is sent to the wheels, the robot will move slowly and hinder its performance. Tests should be run to determine to optimal power supplied to the motors so the Wi-Fi shield does not fail but robot performance is not hindered.Reset TestTests should be run to determine the functionality of the code when the wireless signal is lost. A test should be completed to determine if the robot will automatically reconnect to the network if the router is unplugged and then plugged back in. Another test should also determine if the robot will automatically reconnect to the network if it is taken outside the range of the router and then brought back inside the range. IR Sensor TestThe IR sensors should also be tested. There was no way for the coding team to test the functionality of the IR sensors because not all of the equipment was available at the time. A test should be conducted to make sure an LED on the Arduino lights up when the IR sensors detect a hit. The amount of time the IR LED needs to be on the IR sensor to register a hit and the maximum distance in order to record a hit should also be determined. Programming ScheduleCoding Team Schedule REF _Ref384131433 \h Figure 71 shows the schedule the coding team was working off of while developing the software.Figure STYLEREF 1 \s 7 SEQ Figure \* ARABIC \s 1 1: Gantt Chart Showing Coding Group ScheduleHours Worked REF _Ref384131576 \h Figure 72 shows the estimated hours per week needed to complete the project and the actual hours worked by the coding team. Figure STYLEREF 1 \s 7 SEQ Figure \* ARABIC \s 1 2: Weekly Estimated and Actual Hours Worked REF _Ref384131702 \h Figure 73 shows the total estimated and actual hours spent developing the software. As shown, the actual time spent is far less than the estimated time required to develop the software. This was in large part due to the inaccuracies in estimated the total time required for the project.Figure STYLEREF 1 \s 7 SEQ Figure \* ARABIC \s 1 3: Total Estimated and Actual Hours WorkedCost AnalysisTotal Cost REF _Ref384131728 \h Figure 81 shows the total estimated and actual cost for the software development. It can be seen the project came in well below the estimated cost of developing the software. This was due to the inaccuracies in estimating the time required to complete the project.Figure STYLEREF 1 \s 8 SEQ Figure \* ARABIC \s 1 1: Hours worked by members of the coding team as applied to a cost analysis. ................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download