Hey y’all,
I am working on an underwater drone project that is controlled with an NES controller. I am going to be using an Arduino for the brains of this project, and I was hoping someone might be able to take a look at the code I have used to see if any problems jump out to them!
I have modified NES controller code from a Joseph Corleto, leaving many of his comments in the code for the time being. This is a three T200 thruster set up with two for turning and one for vertical movement. I would love to add a timer feature to the button presses so that they only last up to 3 seconds per button press, but am not sure how to add that in.
#include <Servo.h> // attaches servo lib to file then declares servos
Servo left;
Servo right;
Servo up;
/*
================================================================================
File........... NES Controller Test Code
Purpose........ To demonstrate how to interface to an NES controller
Author......... Joseph Corleto
================================================================================
Notes
================================================================================
- The NES controller contains one 8-bit 4021 shift register inside.
- This register takes parallel inputs and converts them into a serial output.
- This code first latches the data and then shifts in the first bit on the data line.
Then it clocks and shifts in on the data line until all bits are received.
- What is debugged are the button states of the NES controller.
- A logical "1" means the button is not pressed. A logical "0" means the button is
pressed.
- This code shifts the first bit of data into the LSB.
- The order of shifting for the buttons is shown in the table below:
Bit# | Button
--------------
0 | A
--------------
1 | B
--------------
2 | Select
--------------
3 | Start
--------------
4 | Up
--------------
5 | Down
--------------
6 | Left
--------------
7 | Right
--------------
- The NES controller pinout is shown below (looking into controllers
connector end):
__________
/ |
/ O 1 | 1 - Ground
| | 2 - Clock
| 7 O O 2 | 3 - Latch
| | 4 - Data Out
| 6 O O 3 | 5 - No Connection
| | 6 - No Connection
| 5 O O 4 | 7 - 5V
|___________|
*/
//===============================================================================
// Header Files
//===============================================================================
//===============================================================================
// Constants
//===============================================================================
// Here we have a bunch of constants that will become clearer when we look at the
// readNesController() function. Basically, we will use these contents to clear
// a bit. These are chosen according to the table above.
const int A_BUTTON = 0;
const int B_BUTTON = 1;
const int SELECT_BUTTON = 2;
const int START_BUTTON = 3;
const int UP_BUTTON = 4;
const int DOWN_BUTTON = 5;
const int LEFT_BUTTON = 6;
const int RIGHT_BUTTON = 7;
//===============================================================================
// Variables
//===============================================================================
byte nesRegister = 0; // We will use this to hold current button states
byte leftPin = 6; // attach left thruster to pin 6
byte rightPin = 9; // attach right thruster to pin 9
byte upPin = 11; // attach up thrudeter to pin 11
//===============================================================================
// Pin Declarations
//===============================================================================
//Inputs:
int nesData = 4; // The data pin for the NES controller
//Outputs:
int nesClock = 2; // The clock pin for the NES controller
int nesLatch = 3; // The latch pin for the NES controller
//===============================================================================
// Initialization
//===============================================================================
void setup()
{
// Initialize serial port speed for the serial terminal
Serial.begin(9600);
left.attach(leftPin);
right.attach (rightPin);
up.attach (upPin);
// Set appropriate pins to inputs
pinMode(nesData, INPUT);
// Set appropriate pins to outputs
pinMode(nesClock, OUTPUT);
pinMode(nesLatch, OUTPUT);
// Set initial states
digitalWrite(nesClock, LOW);
digitalWrite(nesLatch, LOW);
left.writeMicroseconds(1500); //send stop signial to ESC. Arm the ESC.
right.writeMicroseconds(1500);
up.writeMicroseconds(1500);
delay(4000); //delays so ESC can connect
}
//===============================================================================
// Main
//===============================================================================
void loop()
{
// This function call will return the states of all NES controller's register
// in a nice 8 bit variable format. Remember to refer to the table and
// constants above for which button maps where!
nesRegister = readNesController();
// Slight delay before we debug what was pressed so we don't spam the
// serial monitor.
delay(180);
// To give you an idea on how to use this data to control things for your
// next project, look through the serial terminal code below. Basically,
// just choose a bit to look at and decide what to do whether HIGH (not pushed)
// or LOW (pushed). What is nice about this test code is that we mapped all
// of the bits to the actual button name so no useless memorizing!
if (bitRead(nesRegister, A_BUTTON) == 0) //lifts drone
up.writeMicroseconds(1700);
if (bitRead(nesRegister, B_BUTTON) == 0) //pushes drone down
up.writeMicroseconds(1300);
if (bitRead(nesRegister, START_BUTTON) == 0) //not used in this set up left as is to make sure nothing errors
Serial.println("");
if (bitRead(nesRegister, SELECT_BUTTON) == 0) //not used in this set up left as is to make sure nothing errors
Serial.println("");
if (bitRead(nesRegister, UP_BUTTON) == 0) //pushes drone foward
left.writeMicroseconds(1700);
right.writeMicroseconds(1700);
if (bitRead(nesRegister, DOWN_BUTTON) == 0) //pushes drone backwards
left.writeMicroseconds(1300);
right.writeMicroseconds(1300);
if (bitRead(nesRegister, LEFT_BUTTON) == 0) //turns drone to the left
left.writeMicroseconds(1300);
right.writeMicroseconds(1700);
if (bitRead(nesRegister, RIGHT_BUTTON) == 0) //turns drone to the right
left.writeMicroseconds(1700);
right.writeMicroseconds(1300);
}
//===============================================================================
// Functions
//===============================================================================
///////////////////////
// readNesController //
///////////////////////
byte readNesController()
{
// Pre-load a variable with all 1's which assumes all buttons are not
// pressed. But while we cycle through the bits, if we detect a LOW, which is
// a 0, we clear that bit. In the end, we find all the buttons states at once.
int tempData = 255;
// Quickly pulse the nesLatch pin so that the register grab what it see on
// its parallel data pins.
digitalWrite(nesLatch, HIGH);
digitalWrite(nesLatch, LOW);
// Upon latching, the first bit is available to look at, which is the state
// of the A button. We see if it is low, and if it is, we clear out variable's
// first bit to indicate this is so.
if (digitalRead(nesData) == LOW)
bitClear(tempData, A_BUTTON);
up.writeMicroseconds(1500);
// Clock the next bit which is the B button and determine its state just like
// we did above.
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, B_BUTTON);
up.writeMicroseconds(1500);
// Select button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, SELECT_BUTTON); //not used
// Start button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, START_BUTTON); // not used
// Up button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, UP_BUTTON);
left.writeMicroseconds(1500);
right.writeMicroseconds(1500);
// Down button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, DOWN_BUTTON);
left.writeMicroseconds(1500);
right.writeMicroseconds(1500);
// Left button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, LEFT_BUTTON);
left.writeMicroseconds(1500);
right.writeMicroseconds(1500);
// Right button
digitalWrite(nesClock, HIGH);
digitalWrite(nesClock, LOW);
if (digitalRead(nesData) == LOW)
bitClear(tempData, RIGHT_BUTTON);
left.writeMicroseconds(1500);
right.writeMicroseconds(1500);
// After all of this, we now have our variable all bundled up
// with all of the NES button states.*/
return tempData;
}
The hope is that when the button is no longer pressed it reverts to its stop signal, and i hope over all this makes sense.
Thanks!