-
Notifications
You must be signed in to change notification settings - Fork 135
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #34 from cmavc/master
UnoJoy Car Simulator Wheel
- Loading branch information
Showing
5 changed files
with
735 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,23 @@ | ||
*** | ||
Latest Update | ||
==== | ||
With this update you can now make your own steering wheel and pedal set for the racing game | ||
you always wanted to play with controller! | ||
|
||
You need an MPU6050 IMU sensor for this project, simply connect SDA and SCL pins to A4 and A5 respectively, | ||
VCC -> 5V GND->GND | ||
|
||
or you can use the SDA SCL pins on Arduino close to the GND pin if you need more analog inputs. | ||
You can find the program for the gaming 900 degree wheel set on the UnoJoySteeringWheel folder. | ||
Upload the program and follow the instructions! | ||
|
||
Feel free to contact me for any problems on the program : [email protected] | ||
Mustafa Cem Avci, 17/06/2020 | ||
LONG LIVE OPEN SOURCE! | ||
|
||
YouTube: https://www.youtube.com/watch?v=Rq2QivBzshs | ||
|
||
|
||
UnoJoy | ||
====== | ||
|
||
|
@@ -155,4 +175,4 @@ Using the Deployment Collators | |
To use the Windows one, you'll need to install 7zip (https://www.7-zip.org/), | ||
then add it to your system or user path (search for Environment Variables, then edit | ||
the Path variable and add the 7zip folder). Then you should just be able to use | ||
Windows Deployment Collator.bat. | ||
Windows Deployment Collator.bat. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
/* UnoJoy.h | ||
* Alan Chatham - 2012 | ||
* RMIT Exertion Games Lab | ||
* | ||
* This library gives you a standard way to create Arduino code that talks | ||
* to the UnoJoy firmware in order to make native USB game controllers. | ||
* Functions: | ||
* setupUnoJoy() | ||
* getBlankDataForController() | ||
* setControllerData(dataForController_t dataToSet) | ||
* | ||
* NOTE: You cannot use pins 0 or 1 if you use this code - they are used by the serial communication. | ||
* Also, the setupUnoJoy() function starts the serial port at 38400, so if you're using | ||
* the serial port to debug and it's not working, this may be your problem. | ||
* | ||
* === How to use this library === | ||
* If you want, you can move this file into your Arduino/Libraries folder, then use it like a normal library. | ||
* However, since you'll need to refer to the details of the dataForController_t struct in this file, I would suggest you use | ||
* it by adding it to your Arduino sketch manually (in Arduino, go to Sketch->Add file...) | ||
* | ||
* To use this library to make a controller, you'll need to do 3 things: | ||
* Call setupUnoJoy(); in the setup() block | ||
* Create and populate a dataForController_t type variable and fill it with your data | ||
* The getBlankDataForController() function is good for that. | ||
* Call setControllerData(yourData); where yourData is the variable from above, | ||
* somewhere in your loop(), once you're ready to push your controller data to the system. | ||
* If you forget to call sendControllerData in your loop, your controller won't ever do anything | ||
* | ||
* You can then debug the controller with the included Processing sketch, UnoJoyProcessingVisualizer | ||
* | ||
* To turn it into an actual USB video game controller, you'll reflash the | ||
* Arduino's communication's chip using the instructions found in the 'Firmware' folder, | ||
* then unplug and re-plug in the Arduino. | ||
* | ||
* Details about the dataForController_t type are below, but in order to create and use it, | ||
* you'll declare it like: | ||
* | ||
* dataForController_t sexyControllerData; | ||
* | ||
* and then control button presses and analog stick movement with statements like: | ||
* | ||
* sexyControllerData.triangleOn = 1; // Marks the triangle button as pressed | ||
* sexyControllerData.squareOn = 0; // Marks the square button as unpressed | ||
* sexyControllerData.leftStickX = 90; // Analog stick values can range from 0 - 255 | ||
*/ | ||
|
||
#ifndef UNOJOY_H | ||
#define UNOJOY_H | ||
#include <stdint.h> | ||
#include <util/atomic.h> | ||
#include <Arduino.h> | ||
|
||
// This struct is the core of the library. | ||
// You'll create an instance of this and manipulate it, | ||
// then use the setControllerData function to send that data out. | ||
// Don't change this - the order of the fields is important for | ||
// the communication between the Arduino and it's communications chip. | ||
typedef struct dataForController_t | ||
{ | ||
uint8_t triangleOn : 1; // Each of these member variables | ||
uint8_t circleOn : 1; // control if a button is off or on | ||
uint8_t squareOn : 1; // For the buttons, | ||
uint8_t crossOn : 1; // 0 is off | ||
uint8_t l1On : 1; // 1 is on | ||
uint8_t l2On : 1; | ||
uint8_t l3On : 1; // The : 1 here just tells the compiler | ||
uint8_t r1On : 1; // to only have 1 bit for each variable. | ||
// This saves a lot of space for our type! | ||
uint8_t r2On : 1; | ||
uint8_t r3On : 1; | ||
uint8_t selectOn : 1; | ||
uint8_t startOn : 1; | ||
uint8_t homeOn : 1; | ||
uint8_t dpadLeftOn : 1; | ||
uint8_t dpadUpOn : 1; | ||
uint8_t dpadRightOn : 1; | ||
|
||
uint8_t dpadDownOn : 1; | ||
uint8_t padding : 7; // We end with 7 bytes of padding to make sure we get our data aligned in bytes | ||
|
||
uint8_t leftStickX : 8; // Each of the analog stick values can range from 0 to 255 | ||
uint8_t leftStickY : 8; // 0 is fully left or up | ||
uint8_t rightStickX : 8; // 255 is fully right or down | ||
uint8_t rightStickY : 8; // 128 is centered. | ||
// Important - analogRead(pin) returns a 10 bit value, so if you're getting strange | ||
// results from analogRead, you may need to do (analogRead(pin) >> 2) to get good data | ||
} dataForController_t; | ||
|
||
// Call setupUnoJoy in the setup block of your program. | ||
// It sets up the hardware UnoJoy needs to work properly | ||
void setupUnoJoy(void); | ||
|
||
// You can also call the set | ||
void setupUnoJoy(int); | ||
|
||
// This sets the controller to reflect the button and | ||
// joystick positions you input (as a dataForController_t). | ||
// The controller will just send a zeroed (joysticks centered) | ||
// signal until you tell it otherwise with this function. | ||
void setControllerData(dataForController_t); | ||
|
||
// This function gives you a quick way to get a fresh | ||
// dataForController_t with: | ||
// No buttons pressed | ||
// Joysticks centered | ||
// Very useful for starting each loop with a blank controller, for instance. | ||
// It returns a dataForController_t, so you want to call it like: | ||
// myControllerData = getBlankDataForController(); | ||
dataForController_t getBlankDataForController(void); | ||
|
||
|
||
//----- End of the interface code you should be using -----// | ||
//----- Below here is the actual implementation of | ||
|
||
// This dataForController_t is used to store | ||
// the controller data that you want to send | ||
// out to the controller. You shouldn't mess | ||
// with this directly - call setControllerData instead | ||
dataForController_t controllerDataBuffer; | ||
|
||
// This updates the data that the controller is sending out. | ||
// The system actually works as following: | ||
// The UnoJoy firmware on the ATmega8u2 regularly polls the | ||
// Arduino chip for individual bytes of a dataForController_t. | ||
// | ||
void setControllerData(dataForController_t controllerData){ | ||
// Probably unecessary, but this guarantees that the data | ||
// gets copied to our buffer all at once. | ||
ATOMIC_BLOCK(ATOMIC_FORCEON){ | ||
controllerDataBuffer = controllerData; | ||
} | ||
} | ||
|
||
// serialCheckInterval governs how many ms between | ||
// checks to the serial port for data. | ||
// It shouldn't go above 20 or so, otherwise you might | ||
// get unreliable data transmission to the UnoJoy firmware, | ||
// since after it sends a request, it waits 25 ms for a response. | ||
// If you really need to make it bigger than that, you'll have to | ||
// adjust that timeout in the UnoJoy ATmega8u2 firmware code as well. | ||
volatile int serialCheckInterval = 1; | ||
// This is an internal counter variable to count ms between | ||
// serial check times | ||
int serialCheckCounter = 0; | ||
|
||
// This is the setup function - it sets up the serial communication | ||
// and the timer interrupt for actually sending the data back and forth. | ||
void setupUnoJoy(void){ | ||
// First, let's zero out our controller data buffer (center the sticks) | ||
controllerDataBuffer = getBlankDataForController(); | ||
|
||
// Start the serial port at the specific, low-error rate UnoJoy uses. | ||
// If you want to change the rate, you'll have to change it in the | ||
// firmware for the ATmega8u2 as well. 250,000 is actually the best rate, | ||
// but it's not supported on Macs, breaking the processing debugger. | ||
Serial.begin(38400); | ||
|
||
// Now set up the Timer 0 compare register A | ||
// so that Timer0 (used for millis() and such) | ||
// also fires an interrupt when it's equal to | ||
// 128, not just on overflow. | ||
// This will fire our timer interrupt almost | ||
// every 1 ms (1024 us to be exact). | ||
OCR0A = 128; | ||
TIMSK0 |= (1 << OCIE0A); | ||
} | ||
|
||
// If you really need to change the serial polling | ||
// interval, use this function to initialize UnoJoy. | ||
// interval is the polling frequency, in ms. | ||
void setupUnoJoy(int interval){ | ||
serialCheckInterval = interval; | ||
setupUnoJoy(); | ||
} | ||
|
||
// This interrupt gets called approximately once per ms. | ||
// It counts how many ms between serial port polls, | ||
// and if it's been long enough, polls the serial | ||
// port to see if the UnoJoy firmware requested data. | ||
// If it did, it transmits the appropriate data back. | ||
ISR(TIMER0_COMPA_vect){ | ||
serialCheckCounter++; | ||
if (serialCheckCounter >= serialCheckInterval){ | ||
serialCheckCounter = 0; | ||
// If there is incoming data stored in the Arduino serial buffer | ||
while (Serial.available() > 0) { | ||
pinMode(13, OUTPUT); | ||
//digitalWrite(13, HIGH); | ||
// Get incoming byte from the ATmega8u2 | ||
byte inByte = Serial.read(); | ||
// That number tells us which byte of the dataForController_t struct | ||
// to send out. | ||
Serial.write(((uint8_t*)&controllerDataBuffer)[inByte]); | ||
//digitalWrite(13, LOW); | ||
} | ||
} | ||
} | ||
|
||
// Returns a zeroed out (joysticks centered) | ||
// dataForController_t variable | ||
dataForController_t getBlankDataForController(void){ | ||
// Create a dataForController_t | ||
dataForController_t controllerData; | ||
// Make the buttons zero | ||
controllerData.triangleOn = 0; | ||
controllerData.circleOn = 0; | ||
controllerData.squareOn = 0; | ||
controllerData.crossOn = 0; | ||
controllerData.l1On = 0; | ||
controllerData.l2On = 0; | ||
controllerData.l3On = 0; | ||
controllerData.r1On = 0; | ||
controllerData.r2On = 0; | ||
controllerData.r3On = 0; | ||
controllerData.dpadLeftOn = 0; | ||
controllerData.dpadUpOn = 0; | ||
controllerData.dpadRightOn = 0; | ||
controllerData.dpadDownOn = 0; | ||
controllerData.selectOn = 0; | ||
controllerData.startOn = 0; | ||
controllerData.homeOn = 0; | ||
//Set the sticks to 128 - centered | ||
controllerData.leftStickX = 128; | ||
controllerData.leftStickY = 128; | ||
controllerData.rightStickX = 128; | ||
controllerData.rightStickY = 128; | ||
// And return the data! | ||
return controllerData; | ||
} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* Arduino Gaming Steering Wheel and Pedal Set, using IMU sensor MPU6050 | ||
* An additional improvement on UnoJoy project to work with Racing or Car Simulator Games. | ||
* Project by: M. Cem AVCI | ||
* Date: 17/06/2020 | ||
* [email protected] | ||
* | ||
******************************************************************************************* | ||
* Functions Added to UnoJoyArduinoSample.ino: | ||
* setupIMU function sets up MPU6050 to the max. of +- 500 degree rotation, a normal steering wheel rotates about +- 450 degree so thats fine. | ||
* rotAngle function actually calculates the rotation angle but remember the position of the IMU is critical. You may need to use different | ||
* gyro angle depending on how you made the setup. | ||
* once your setup is done, upload this code to arduino while centering the wheel. The rest of the process is the same as transforming into PS3 controller. | ||
* More details on: | ||
* https://www.youtube.com/watch?v=5q3cpu5xVu4 | ||
*/ | ||
|
||
#include <Wire.h> | ||
#include <UnoJoy.h> | ||
|
||
long gyroX,gyroY,gyroZ; | ||
float rotAngle_X,rotAngle_Y,rotAngle_Z; | ||
|
||
|
||
void setup() { | ||
setupPins(); | ||
setupUnoJoy(); | ||
setupIMU(); | ||
Serial.begin(9600); | ||
|
||
|
||
|
||
} | ||
|
||
void loop() { | ||
dataForController_t controllerData = getControllerData(); | ||
setControllerData(controllerData); | ||
|
||
|
||
} | ||
void setupPins(void){ | ||
// Set all the digital pins as inputs | ||
// with the pull-up enabled, except for the | ||
// two serial line pins | ||
for (int i = 2; i <= 12; i++){ | ||
pinMode(i, INPUT); | ||
digitalWrite(i, HIGH); | ||
} | ||
pinMode(A4, INPUT); | ||
digitalWrite(A4, HIGH); | ||
pinMode(A5, INPUT); | ||
digitalWrite(A5, HIGH); | ||
} | ||
|
||
void setupIMU(void){ | ||
Wire.begin(); | ||
Wire.beginTransmission(0b1101000); | ||
Wire.write(0x6B); | ||
Wire.write(0b00000000); | ||
Wire.endTransmission(); | ||
Wire.beginTransmission(0b1101000); | ||
Wire.write(0x1B); | ||
Wire.write(0x00001000); // -+ 500 degree | ||
Wire.endTransmission(); | ||
|
||
} | ||
|
||
|
||
dataForController_t getControllerData(void){ | ||
float steeringWheel=rotAngle(); | ||
// Set up a place for our controller data | ||
// Use the getBlankDataForController() function, since | ||
// just declaring a fresh dataForController_t tends | ||
// to get you one filled with junk from other, random | ||
// values that were in those memory locations before | ||
dataForController_t controllerData = getBlankDataForController(); | ||
// Since our buttons are all held high and | ||
// pulled low when pressed, we use the "!" | ||
// operator to invert the readings from the pins | ||
controllerData.triangleOn = !digitalRead(2); | ||
controllerData.circleOn = !digitalRead(3); | ||
controllerData.squareOn = !digitalRead(4); | ||
controllerData.crossOn = !digitalRead(5); | ||
controllerData.dpadUpOn = !digitalRead(6); | ||
controllerData.dpadDownOn = !digitalRead(7); | ||
controllerData.dpadLeftOn = !digitalRead(8); | ||
controllerData.dpadRightOn = !digitalRead(9); | ||
controllerData.l1On = !digitalRead(10); | ||
controllerData.r1On = !digitalRead(11); | ||
controllerData.selectOn = !digitalRead(12); | ||
controllerData.startOn = !digitalRead(A4); | ||
controllerData.homeOn = !digitalRead(A5); | ||
|
||
// Set the analog sticks | ||
// Since analogRead(pin) returns a 10 bit value, | ||
// we need to perform a bit shift operation to | ||
// lose the 2 least significant bits and get an | ||
// 8 bit number that we can use | ||
controllerData.leftStickX = steeringWheel; | ||
controllerData.leftStickY = analogRead(A0) >> 2; | ||
controllerData.rightStickX = analogRead(A1) >> 2; | ||
controllerData.rightStickY = analogRead(A2) >> 2; | ||
// And return the data! | ||
return controllerData; | ||
} | ||
|
||
|
||
float rotAngle(){ | ||
|
||
Wire.beginTransmission(0b1101000); | ||
Wire.write(0x43); | ||
Wire.endTransmission(); | ||
Wire.requestFrom(0b1101000,6); | ||
while(Wire.available() < 6){}; | ||
gyroX = Wire.read()<<8|Wire.read(); | ||
gyroY = Wire.read()<<8|Wire.read(); | ||
gyroZ = Wire.read()<<8|Wire.read(); | ||
|
||
rotAngle_X = gyroX / 655.0; | ||
rotAngle_Y = gyroY / 655.0; | ||
rotAngle_Z = gyroZ / 655.0; | ||
|
||
rotAngle_Z = map(rotAngle_Z,-450,450,52,200); | ||
|
||
return rotAngle_Z; | ||
delay(10); | ||
} |
Oops, something went wrong.