diff --git a/ControllerBuffer.cpp b/ControllerBuffer.cpp deleted file mode 100644 index dbce583..0000000 --- a/ControllerBuffer.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "ControllerBuffer.h" -#include "PinMappings.h" -#include "Debug.h" - -// buffer to hold data being read from controller -bool buffer[DATA_SIZE + DATA_OFFSET]; - -// bit resolution and offsets -int bitOffsets[32]; -int bitResolution; - -/** Function to send a Command to the attached N64-Controller. - * Must be run from RAM to defy timing differences introduced from - * reading Code from ESP32's SPI Flash Chip. -*/ -void IRAM_ATTR sendCommand(byte command) -{ - // the current bit to write - bool bit; - - // clear output buffer - memset(buffer,0,DATA_SIZE + DATA_OFFSET); - - // for each bit - for (int i = 0; i < 8; i++) - { - // get value - bit = (1 << (7 - i)) & command; - - // write data - LINE_WRITE_LOW; - delayMicroseconds((3 - 2 * bit)); - LINE_WRITE_HIGH; - delayMicroseconds((1 + 2 * bit)); - } - - // console stop bit - LINE_WRITE_LOW; - delayMicroseconds(1); - LINE_WRITE_HIGH; - delayMicroseconds(2); - - // read returned data as fast as possible - for(int i = 0;i < DATA_SIZE + DATA_OFFSET;i++) - { - buffer[i] = digitalRead(DATA_PIN); - } - - // plot polling process from controller if unstructed to - #ifdef PLOT_CONSOLE_POLLING - for(int i = 0; i < DATA_SIZE + DATA_OFFSET;i++) - { - Serial.println(buffer[i]*2500); - } - #endif -} - -/* Function to extract a controller bit from the buffer of returned data */ -void getBit(bool *bit,int offset,bool *data) -{ - // sanity check offset - if(offset < 0) offset = 0; - - // count - short count = 0; - - // get count from offset to offset + length - for(int i = offset + DATA_OFFSET;i < offset + bitResolution;i++) - { - count += *(data + i); - } - - // if offset surpasses threshold set bit - *bit = false; - if(count > BIT_THRESHOLD) *bit = true; -} - -/** Function to populate the controller struct if command 0x01 was sent. - * Buttons are set according to data in buffer, raw axis data is written, - * Axis Data is correctly decoded from raw axis data by taking two's complement - * and checking if value if below 'MAX_INCLINE_AXIS_X' or 'MAX_INCLINE_AXIS_Y'. - * If values surpass the maximum incline they are set to match those values. - */ -void populateControllerStruct(ControllerData *data) -{ - // first byte - getBit(&(data->buttonA) , bitOffsets[0] ,&buffer[0]); - getBit(&(data->buttonB), bitOffsets[1] ,&buffer[0]); - getBit(&(data->buttonZ), bitOffsets[2] ,&buffer[0]); - getBit(&(data->buttonStart), bitOffsets[3] ,&buffer[0]); - getBit(&(data->DPadUp), bitOffsets[4] ,&buffer[0]); - getBit(&(data->DPadDown), bitOffsets[5] ,&buffer[0]); - getBit(&(data->DPadLeft), bitOffsets[6] ,&buffer[0]); - getBit(&(data->DPadRight), bitOffsets[7] ,&buffer[0]); - - // second byte, first two bits are unused - getBit(&(data->buttonL), bitOffsets[10] ,&buffer[0]); - getBit(&(data->buttonR), bitOffsets[11] ,&buffer[0]); - getBit(&(data->CUp), bitOffsets[12] ,&buffer[0]); - getBit(&(data->CDown), bitOffsets[13] ,&buffer[0]); - getBit(&(data->CLeft), bitOffsets[14] ,&buffer[0]); - getBit(&(data->CRight), bitOffsets[15] ,&buffer[0]); - - // third byte - getBit(&(data->xAxisRaw[0]), bitOffsets[16], &buffer[0]); - getBit(&(data->xAxisRaw[1]), bitOffsets[17], &buffer[0]); - getBit(&(data->xAxisRaw[2]), bitOffsets[18], &buffer[0]); - getBit(&(data->xAxisRaw[3]), bitOffsets[19], &buffer[0]); - getBit(&(data->xAxisRaw[4]), bitOffsets[20], &buffer[0]); - getBit(&(data->xAxisRaw[5]), bitOffsets[21], &buffer[0]); - getBit(&(data->xAxisRaw[6]), bitOffsets[22], &buffer[0]); - getBit(&(data->xAxisRaw[7]), bitOffsets[23], &buffer[0]); - - // fourth byte - getBit(&(data->yAxisRaw[0]), bitOffsets[24], &buffer[0]); - getBit(&(data->yAxisRaw[1]), bitOffsets[25], &buffer[0]); - getBit(&(data->yAxisRaw[2]), bitOffsets[26], &buffer[0]); - getBit(&(data->yAxisRaw[3]), bitOffsets[27], &buffer[0]); - getBit(&(data->yAxisRaw[4]), bitOffsets[28], &buffer[0]); - getBit(&(data->yAxisRaw[5]), bitOffsets[29], &buffer[0]); - getBit(&(data->yAxisRaw[6]), bitOffsets[30], &buffer[0]); - getBit(&(data->yAxisRaw[7]), bitOffsets[31], &buffer[0]); - - // sum up bits to get axis bytes - data->xAxis = 0; - data->yAxis = 0; - for(int i = 0;i < 8;i++) - { - data->xAxis += (data->xAxisRaw[i] * (0x80 >> (i))); - data->yAxis += (data->yAxisRaw[i] * (0x80 >> (i))); - - // print y axis values - #ifdef PRINT_Y_AXIS_VALUES - Serial.printf("%i %i %i %i %i %i %i %i\n",data->yAxisRaw[0],data->yAxisRaw[1],data->yAxisRaw[2],data->yAxisRaw[3],data->yAxisRaw[4],data->yAxisRaw[5],data->yAxisRaw[6],data->yAxisRaw[7]); - Serial.printf("yAxis: %i \n",data->yAxis); - #endif - - // print x axis values - #ifdef PRINT_X_AXIS_VALUES - Serial.printf("%i %i %i %i %i %i %i %i\n",data->xAxisRaw[0],data->xAxisRaw[1],data->xAxisRaw[2],data->xAxisRaw[3],data->xAxisRaw[4],data->xAxisRaw[5],data->xAxisRaw[6],data->xAxisRaw[7]); - Serial.printf("xAxis: %i \n",data->xAxis); - #endif - } - - // decode xAxis two's complement - if(data->xAxis & 0x80) - { - data->xAxis = -1 * (0xff - data->xAxis); - } - - // decode yAxis two's complement - if(data->yAxis & 0x80) - { - data->yAxis = -1 * (0xff - data->yAxis); - } - - // keep x axis below maxIncline - if(data->xAxis > MAX_INCLINE_AXIS_X) data->xAxis = MAX_INCLINE_AXIS_X; - if(data->xAxis < -MAX_INCLINE_AXIS_X) data->xAxis = -MAX_INCLINE_AXIS_X; - - // keep y axis below maxIncline - if(data->yAxis > MAX_INCLINE_AXIS_Y) data->yAxis = MAX_INCLINE_AXIS_Y; - if(data->yAxis < -MAX_INCLINE_AXIS_Y) data->yAxis = -MAX_INCLINE_AXIS_Y; - - //Serial.printf("xaxis: %-3i yaxis: %-3i \n",data->xAxis,data->yAxis); -} - -void updateOffsetsAndResolution() -{ - // the current bit counter - int bitCounter = 0; - - // to hold the offset of A Button's falling edge - int bitAfallingOffset = 0; - - // iterate over buffer - for(int i = 0;i < DATA_SIZE + DATA_OFFSET - 1;i++) - { - // if a falling edge is detected - if(buffer[i] == true && buffer[1+i] == false) - { - // store bit's end offset - bitOffsets[bitCounter] = i+1; - - // if it's the A button store offset of the falling edge - if(bitCounter == 0) bitAfallingOffset = i+1; - - // if it's the B button calculate the bit Resolution - if(bitCounter == 1) bitResolution = (i+1) - bitAfallingOffset; - - // increment bit counter - bitCounter++; - } - } - - Serial.printf("Bit resolution is %i \n",bitResolution); - - // calculate bit's beginning offsets by subtracting resolution - for(int i = 0;i < 32;i++) - { - bitOffsets[i] -= bitResolution; - Serial.printf("beginning of bit %i detected @ begin+%i \n",i+1,bitOffsets[i]); - } - - -} \ No newline at end of file diff --git a/ControllerBuffer.h b/ControllerBuffer.h deleted file mode 100644 index 1489cde..0000000 --- a/ControllerBuffer.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CONTROLLER_BUFFER_H -#define CONTROLLER_BUFFER_H - -#include "ControllerData.h" -#include - -#define DATA_SIZE 450 // number of sample points to poll -#define DATA_OFFSET 0 // number of samples to ignore after staring to poll - -#define MAX_INCLINE_AXIS_X 60 -#define MAX_INCLINE_AXIS_Y 60 - -#define BIT_THRESHOLD 6 - -extern void IRAM_ATTR sendCommand(byte command); -extern void populateControllerStruct(ControllerData *data); - -/** Function to read the offsets of the individual bits so that they can be updated for reading. - * Resolution is detected by taking the distance of two falling edges of Button A and B. - * Buttons must not be pressed by the time this command is invoked ! - * sendCommand(0x01) must be invoked before ! - */ -extern void updateOffsetsAndResolution(); - - -#endif \ No newline at end of file diff --git a/ControllerData.h b/ControllerData.h deleted file mode 100644 index f546ed3..0000000 --- a/ControllerData.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CONTROLLER_DATA_H -#define CONTROLLER_DATA_H - -struct ControllerData -{ - bool buttonA; - bool buttonB; - bool buttonZ; - bool buttonL; - bool buttonR; - bool buttonStart; - - bool DPadUp; - bool DPadDown; - bool DPadLeft; - bool DPadRight; - - bool CUp; - bool CDown; - bool CLeft; - bool CRight; - - short xAxis; - short yAxis; - - bool xAxisRaw[8]; - bool yAxisRaw[8]; - -}; - -#endif \ No newline at end of file diff --git a/Debug.h b/Debug.h deleted file mode 100644 index 0775613..0000000 --- a/Debug.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef DEBUG_H -#define DEBUG_H - -//#define PLOT_CONSOLE_POLLING -//#define PRINT_X_AXIS_VALUES -//#define PRINT_Y_AXIS_VALUES - -#endif \ No newline at end of file diff --git a/Output.cpp b/Output.cpp deleted file mode 100644 index 9e00bca..0000000 --- a/Output.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "Output.h" -#include "PinMappings.h" - -#include - -int encoderXpos = 0; -int encoderYpos = 0; - -void setupIO() -{ - // the controller data line - LINE_WRITE_HIGH; - - pinMode(PIN_BUTTON_A,OUTPUT); - pinMode(PIN_BUTTON_B,OUTPUT); - pinMode(PIN_BUTTON_Z,OUTPUT); - pinMode(PIN_BUTTON_S,OUTPUT); - pinMode(PIN_BUTTON_L,OUTPUT); - pinMode(PIN_BUTTON_R,OUTPUT); - - pinMode(PIN_DPAD_UP, OUTPUT); - pinMode(PIN_DPAD_DOWN, OUTPUT); - pinMode(PIN_DPAD_LEFT, OUTPUT); - pinMode(PIN_DPAD_RIGHT,OUTPUT); - - pinMode(PIN_C_UP, OUTPUT); - pinMode(PIN_C_DOWN, OUTPUT); - pinMode(PIN_C_LEFT, OUTPUT); - pinMode(PIN_C_RIGHT,OUTPUT); - - pinMode(PIN_A_AXIS_X, OUTPUT); - pinMode(PIN_B_AXIS_X, OUTPUT); - - pinMode(PIN_A_AXIS_Y, OUTPUT); - pinMode(PIN_B_AXIS_Y, OUTPUT); -} - -/** Function to simulate the 90° phaseshifted quadrature encoding used by - * the N64-Controller's joystick's linear encoders. As only change in position - * (not absolute position) is reported the change in the value between writes - * is needed for calculation. Must be run from RAM to mitigate timing differences in - * ESP32's SPI-Flash Chip Access Times. - * @param relativeMovement should be processed by calling 'mapJoystickToEncoderPos' - * with the read joystick position. - */ -void IRAM_ATTR moveAxis(int axisPinA,int axisPinB,int requestedEncoderPos,int *realEncoderPos) -{ - // get difference from requested ender position to real encoder position - int difference = requestedEncoderPos - *realEncoderPos; - - // store new real encoder position - *realEncoderPos = requestedEncoderPos; - - // set pins used for outputting - int pinA = axisPinA; - int pinB = axisPinB; - - // invert pins if moving in the other direction - if(difference > 0) - { - pinA = axisPinB; - pinB = axisPinA; - } - - // get absolute movement value - difference = abs(difference); - - // for each change in movement - for(int i = 0;i < difference;i++) - { - // replicate 90° phaseshifted quadrate output - digitalWrite(pinA,LOW); - delayMicroseconds(2); - digitalWrite(pinB,LOW); - delayMicroseconds(6); - digitalWrite(pinA,HIGH); - delayMicroseconds(2); - digitalWrite(pinB,HIGH); - - // debounce as N64 will not detect change correctly otherwise - delayMicroseconds(40); - } -} - -/** Function to map Joystick position to relative Encoder Position. - * According to Nintendo the maximum value for any axis to be assumed should - * be +- 60. The encoder starts acting strange after ~ 30 turns in a given - * direction. Also passing on ControllerData struct so that user specified - * modifiers can be used to specify alternate mapping, eg. for - * easier ESS-Position in Ocarina of Time (see my example) - */ -int mapJoystickToEncoderPos(int joyval,ControllerData *data) -{ - // ESS-Modifier for Ocarina of Time, hold A+B to stay in ESS-Position - if(data->buttonA && data->buttonB) return -2; - - // deadzone handling - if(abs(joyval) < 3) return 0; - - // normally return half ??? - return (int)(joyval * .5f); -} - -void outputToiQue(ControllerData *data) -{ - // ===== Write Button Data ===== - digitalWrite(PIN_BUTTON_A,data->buttonA); - digitalWrite(PIN_BUTTON_B,data->buttonB); - digitalWrite(PIN_BUTTON_L,data->buttonL); - digitalWrite(PIN_BUTTON_R,data->buttonR); - digitalWrite(PIN_BUTTON_Z,data->buttonZ); - digitalWrite(PIN_BUTTON_S,data->buttonStart); - - // ===== Write D-Pad Data ===== - digitalWrite(PIN_DPAD_UP, data->DPadUp); - digitalWrite(PIN_DPAD_DOWN, data->DPadDown); - digitalWrite(PIN_DPAD_LEFT, data->DPadLeft); - digitalWrite(PIN_DPAD_RIGHT,data->DPadRight); - - // ===== Write C-Button Data ===== - digitalWrite(PIN_C_UP, data->CUp); - digitalWrite(PIN_C_DOWN, data->CDown); - digitalWrite(PIN_C_LEFT, data->CLeft); - digitalWrite(PIN_C_RIGHT, data->CRight); - - // ===== Write Joystick Data ===== - - // calculate change in axis between writes - //int xAxisChange = data->xAxis - lastxAxis; - //int yAxisChange = data->yAxis - lastyAxis; - - // move axis accordingly - moveAxis(PIN_A_AXIS_X,PIN_B_AXIS_X,mapJoystickToEncoderPos(data->xAxis,data),&encoderXpos); - moveAxis(PIN_A_AXIS_Y,PIN_B_AXIS_Y,mapJoystickToEncoderPos(data->yAxis,data),&encoderYpos); -} \ No newline at end of file diff --git a/Output.h b/Output.h deleted file mode 100644 index 6e71579..0000000 --- a/Output.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef OUTPUT_H -#define OUTPUT_H - -#include "ControllerData.h" - -/** Function to output Data stored in ControllerData struct to the iQue console. - * Buttons states are directly set usig digitalWrite(), Axis Data is written by - * Simulating the 90° shifted quadrate encoding used by the N64 joysticks. - * As these operate on a relative basis the difference between the current - * and the last joystick positions are calculated and simulated. - */ -extern void outputToiQue(ControllerData *data); - -/** Function to setup the IO used for interfacing with the N64-Controller - * and the iQue-Console - */ -extern void setupIO(); - -#endif \ No newline at end of file diff --git a/PinMappings.h b/PinMappings.h deleted file mode 100644 index 67746ce..0000000 --- a/PinMappings.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef PIN_MAPPINGS_H -#define PIN_MAPPINGS_H - -#define DATA_PIN 13 - -#define PIN_BUTTON_A 15 -#define PIN_BUTTON_B 5 -#define PIN_BUTTON_Z 23 -#define PIN_BUTTON_S 1 -#define PIN_BUTTON_L 1 -#define PIN_BUTTON_R 1 - -#define PIN_DPAD_UP 1 -#define PIN_DPAD_DOWN 1 -#define PIN_DPAD_LEFT 1 -#define PIN_DPAD_RIGHT 1 - -#define PIN_C_UP 1 -#define PIN_C_DOWN 1 -#define PIN_C_LEFT 1 -#define PIN_C_RIGHT 1 - -#define PIN_A_AXIS_X 14 -#define PIN_B_AXIS_X 27 - -#define PIN_A_AXIS_Y 2 -#define PIN_B_AXIS_Y 4 - -#define LINE_WRITE_HIGH pinMode(DATA_PIN,INPUT_PULLUP) -#define LINE_WRITE_LOW pinMode(DATA_PIN,OUTPUT) - - -#endif \ No newline at end of file diff --git a/iQue.ino b/iQue.ino deleted file mode 100644 index e63c010..0000000 --- a/iQue.ino +++ /dev/null @@ -1,39 +0,0 @@ -#include "Output.h" -#include "ControllerData.h" -#include "ControllerBuffer.h" -#include "Debug.h" - -ControllerData controller; - -void setup() -{ - Serial.begin(115200); - - // setup io pins - setupIO(); - - #ifdef PLOT_CONSOLE_POLLING - delay(5000); - sendCommand(0x01); - while(true); - #endif - - sendCommand(0x01); - updateOffsetsAndResolution(); -} - - -void loop() -{ - // send command 0x01 to n64 controller - sendCommand(0x01); - - // store received data in controller struct - populateControllerStruct(&controller); - - // output received data to ique - outputToiQue(&controller); - - // polling must not occur faster than every 20 ms - delay(14); -} \ No newline at end of file