Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jogcon motor #26

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions examples/JogconMotor/JogconMotor.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*******************************************************************************
* This file is part of PsxNewLib. *
* *
* Copyright (C) 2019-2020 by SukkoPera <[email protected]> *
* *
* PsxNewLib is free software: you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* PsxNewLib is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with PsxNewLib. If not, see http://www.gnu.org/licenses. *
*******************************************************************************
*
* This sketch can send commands to a PSX Jogcon controller to move it's motor
*
* This example drives the controller through the hardware SPI port, so pins are
* fixed and depend on the board/microcontroller being used. For instance, on an
* Arduino Uno connections must be as follows:
*
* CMD: Pin 11
* DATA: Pin 12
* CLK: Pin 13
*
* Any pin can be used for ATTN, but please note that most 8-bit AVRs require
* the HW SPI SS pin to be kept as an output for HW SPI to be in master mode, so
* using that pin for ATTN is a natural choice. On the Uno this would be pin 10.
*
* It also works perfectly on OpenPSX2AmigaPadAdapter boards (as it's basically
* a modified Uno).
*
*/

#include <PsxControllerHwSpi.h>

const byte PIN_PS2_ATT = 10;
PsxControllerHwSpi<PIN_PS2_ATT> psx;

void setup() {
Serial.begin(115200);
while(!Serial){}
Serial.println(F("Ready!"));
}

void loop() {
static bool haveController = false;
static uint8_t force = 8;

if (!haveController) {
if (psx.begin()) {
Serial.println(F("Controller found!"));
haveController = true;

delay(300);

if (!psx.enterConfigMode ()) {
Serial.println (F("Cannot enter config mode"));
} else {
//must enable analog mode to use jogcon's paddle
if (!psx.enableAnalogSticks ())
Serial.println (F("Cannot enable analog sticks"));

if (!psx.enableAnalogButtons ())
Serial.println (F("Cannot enable analog buttons"));

//must enable rumble to use the jogcon's motor
if (!psx.enableRumble ())
Serial.println (F("Cannot enable rumble"));

if (!psx.exitConfigMode ())
Serial.println (F("Cannot exit config mode"));
}
psx.read (); // Make sure the protocol is up to date
}
} else {
if(!psx.read()){
haveController = false;
} else if (psx.getProtocol () == PSPROTO_JOGCON) {// It's a jogcon!

//reading the "raw" values
uint8_t jogPosition = 0;
uint8_t jogRevolutions = 0;
JogconDirection jogDirection;
JogconCommand cmdResult;

//Checking if the jog state has changed or theres as command result.
//Not required but just to not fill the serial output when controller is idle.
if (psx.getJogconData(jogPosition, jogRevolutions, jogDirection, cmdResult) && (jogDirection != JOGCON_DIR_NONE || cmdResult != JOGCON_CMD_NONE)) {
Serial.print(F("Position: "));
Serial.print(jogPosition);
Serial.print(F("\tRevolutions: "));
Serial.print(jogRevolutions);

if (jogDirection != JOGCON_DIR_NONE) {
Serial.print(F("\tRotation: "));
if (jogDirection == JOGCON_DIR_CCW)
Serial.print(F("CCW"));
else if (jogDirection == JOGCON_DIR_CW)
Serial.print(F("CW"));
else if (jogDirection == JOGCON_DIR_MAX)
Serial.print(F("MAX"));
else
Serial.print(psx.getAnalogButton(PSAB_PAD_UP), HEX);
}

if (cmdResult != JOGCON_CMD_NONE) {
Serial.print(F("\tCmdResult: "));
if (cmdResult == JOGCON_CMD_DROP_REVOLUTIONS)
Serial.print(F("DROP_REVOLUTIONS"));
else if (cmdResult == JOGCON_CMD_NEW_START)
Serial.print(F("NEW_START"));
else
Serial.print(psx.getAnalogButton(PSAB_PAD_UP), HEX);
}
Serial.println();
} // end getJogconData()


//buttons to decrement/increment the motor's force
if (psx.buttonJustPressed (PSB_L2)) {
force--;
if (force > 15)
force = 0;
Serial.print(F("Motor force: "));
Serial.println(force);
} else if (psx.buttonJustPressed (PSB_R2)) { //increment the motor's force
force++;
if (force > 15)
force = 15;
Serial.print(F("Motor force: "));
Serial.println(force);
}


//Send command no jogcon. Will be sent on next Read()
JogconDirection newDirection = JOGCON_DIR_NONE;
JogconCommand newCommand = JOGCON_CMD_NONE;

//buttons to change the jogcon's mode

//rotate to start position
if (psx.buttonPressed (PSB_PAD_UP)) //PSB_TRIANGLE
newDirection = JOGCON_DIR_START;
//rotate CCW
else if (psx.buttonPressed (PSB_PAD_LEFT)) //PSB_SQUARE
newDirection = JOGCON_DIR_CCW;
//rotate CW
else if (psx.buttonPressed (PSB_PAD_RIGHT)) // PSB_CIRCLE
newDirection = JOGCON_DIR_CW;

//set new start position (and forget the ammount of revolutions)
if (psx.buttonPressed (PSB_CROSS))
newCommand = JOGCON_CMD_NEW_START;
//forget the ammount of revolutions
else if (psx.buttonPressed (PSB_TRIANGLE))
newCommand = JOGCON_CMD_DROP_REVOLUTIONS;

psx.setJogconMotorMode(newDirection, newCommand, force);

} //end if PSPROTO_JOGCON

}

}
122 changes: 120 additions & 2 deletions src/PsxNewLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,26 @@ enum GunconStatus {
GUNCON_OTHER_ERROR
};

//! \brief Jogcon rotation direction
enum JogconDirection {
JOGCON_DIR_NONE = 0x0,
JOGCON_DIR_CW = 0x1,
JOGCON_DIR_CCW = 0x2,
JOGCON_DIR_START = 0x3,
//Bellow values are only used as return on getJogconData()
JOGCON_DIR_MAX = 0x4, //Position and revolutions maxed out
JOGCON_DIR_OTHER = 0xF //Using 0x0F as generic unhandled code
};

//! \brief Jogcon command
enum JogconCommand {
JOGCON_CMD_NONE = 0x0,
JOGCON_CMD_DROP_REVOLUTIONS = 0x80,
JOGCON_CMD_NEW_START = 0xC0,
//Bellow values are only used as return on getJogconData()
JOGCON_CMD_OTHER = 0xF0 //Using 0xF0 as generic unhandled code return
};

/** \brief PSX Controller Interface
*
* This is the base class implementing interactions with PSX controllers. It is
Expand Down Expand Up @@ -377,6 +397,12 @@ class PsxController {
*/
byte motor2Level;

/** \brief requested jogcon motor power and mode.
*
* Rumble must be enabled and 7.5v supplied to pin 3!
*/
byte jogconMotorLevelAndMode;

/** \brief Assert the Attention line
*
* This function must be implemented by derived classes and must set the
Expand Down Expand Up @@ -584,6 +610,7 @@ class PsxController {
rumbleEnabled = false;
motor1Level = 0x00;
motor2Level = 0x00;
jogconMotorLevelAndMode = 0x00;

// Some disposable readings to let the controller know we are here
for (byte i = 0; i < 5; ++i) {
Expand Down Expand Up @@ -901,8 +928,12 @@ class PsxController {
if(rumbleEnabled) {
byte out[sizeof (poll)];
memcpy(out, poll, sizeof(poll));
out[3] = motor1Level;
out[4] = motor2Level;
if (protocol == PSPROTO_JOGCON) {
out[3] = jogconMotorLevelAndMode;
} else {
out[3] = motor1Level;
out[4] = motor2Level;
}
in = autoShift (out, sizeof(poll));
}
else {
Expand Down Expand Up @@ -1014,6 +1045,12 @@ class PsxController {

// Bring to the usual 0-255 range
lx += 0x80;

//Stores the "raw" data to use on getJogconData().
//Reusing the analogButtonData array. Need to make a new structure to hold those values.
analogButtonData[PSAB_PAD_RIGHT] = in[5];
analogButtonData[PSAB_PAD_LEFT] = in[6];
analogButtonData[PSAB_PAD_UP] = in[7];
break;
default:
// We are already done
Expand Down Expand Up @@ -1229,6 +1266,87 @@ class PsxController {

return status;
}


/** \brief Set Jogcon direction, command and motor power
*
* Data will be combined into a single byte as ddccffff, where
* dd = direction (2 bits)
* cc = command (2 bits)
* ffff = force (4 bits)
*
* \param[in] direction The direction for motor rotation
* \param[in] command The Command to be sent
* \param[in] motorPower The amount of motor power. Max 15 (0x0F).
*/
void setJogconMotorMode (JogconDirection direction, JogconCommand command, const uint8_t motorPower) {
if((byte)direction > 0x3)
direction = JOGCON_DIR_NONE;

if((byte)command == JOGCON_CMD_OTHER)
command = JOGCON_CMD_NONE;

jogconMotorLevelAndMode = ((byte)direction << 4) | (command | (motorPower & 0x0F));
}


/** \brief Retrieve Jogcon state and raw readings
*
* \param[in] position A variable where the jog position will be stored
* \param[in] revolutions A variable where the jog revolutions will be stored
* \param[in] direction A variable where the last direction will be stored
* \param[in] cmdResult A variable where the last command sent will be stored
*
* \return true if the device is Jogcon and data is valid
*/
bool getJogconData (uint8_t& position, uint8_t& revolutions, JogconDirection& direction, JogconCommand& cmdResult) const {
if (protocol == PSPROTO_JOGCON && analogSticksValid) {
position = analogButtonData[PSAB_PAD_RIGHT];
revolutions = analogButtonData[PSAB_PAD_LEFT];
//state = static_cast<JogconRotation>(analogButtonData[PSAB_PAD_UP]);

//State byte contains two nibbles with data.
//Rotation state and command result

//Last rotation direction
switch (analogButtonData[PSAB_PAD_UP] & 0x0F) {
case 0x0:
direction = JOGCON_DIR_NONE;
break;
case 0x1:
direction = JOGCON_DIR_CW;
break;
case 0x2:
direction = JOGCON_DIR_CCW;
break;
case 0x4:
direction = JOGCON_DIR_MAX; //Max value reached (overflow)
break;
default:
direction = JOGCON_DIR_OTHER; //Other - unhandled
break;
}

//Last command result
switch (analogButtonData[PSAB_PAD_UP] & 0xF0) {
case 0x00:
cmdResult = JOGCON_CMD_NONE;
break;
case 0x80:
cmdResult = JOGCON_CMD_DROP_REVOLUTIONS;
break;
case 0xC0:
cmdResult = JOGCON_CMD_NEW_START;
break;
default:
cmdResult = JOGCON_CMD_OTHER; //Other - unhandled
break;
}
return true;
}

return false;
}

//! @} // Polling Functions
};
Expand Down