From 5c4836a553fb1b9e9a5e89df92bf9cc376c1f921 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Fri, 20 Mar 2026 19:58:12 -0500 Subject: [PATCH 1/4] Setup of rumble task --- include/player/ps3-controller.h | 6 +++++ include/player/rumble.h | 15 ++++++++++++ src/player/ps3-controller.cpp | 42 +++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 include/player/rumble.h diff --git a/include/player/ps3-controller.h b/include/player/ps3-controller.h index f0ffd78..8218a48 100644 --- a/include/player/ps3-controller.h +++ b/include/player/ps3-controller.h @@ -3,6 +3,7 @@ #include #include "player/controller.h" +#include "player/rumble.h" namespace Player { @@ -34,6 +35,8 @@ namespace Player AnalogStick leftAnalog() override; AnalogStick rightAnalog() override; + void rumble(RumbleOptions option); + const uint8_t rawButtonState(const ControllerButton button) const; const bool wasPressed(const ControllerButton button) const; const bool wasPressedAndReleased(const ControllerButton button) const; @@ -45,6 +48,9 @@ namespace Player ::Ps3Controller *controller; static Ps3Controller *instance; + static void playRumblePattern(RumbleStep *pattern, int steps); + static void rumbleTask(void *pvParameters); + void triggerRumble(RumbleStep *pattern); void handleOnConnect(); static void onConnect() { diff --git a/include/player/rumble.h b/include/player/rumble.h new file mode 100644 index 0000000..2265e19 --- /dev/null +++ b/include/player/rumble.h @@ -0,0 +1,15 @@ +#pragma once + +namespace Player +{ + enum class RumbleOptions + { + DoublePulse + }; + + struct RumbleStep + { + int intensity; + int duration; + }; +} \ No newline at end of file diff --git a/src/player/ps3-controller.cpp b/src/player/ps3-controller.cpp index ef14e97..8b13fa2 100644 --- a/src/player/ps3-controller.cpp +++ b/src/player/ps3-controller.cpp @@ -41,6 +41,17 @@ namespace Player return joystick; } + void Ps3Controller::rumble(RumbleOptions option) + { + switch (option) + { + case RumbleOptions::DoublePulse: + break; + default: + break; + } + } + const uint8_t Ps3Controller::rawButtonState(const ControllerButton button) const { if (!instance->connection) @@ -160,5 +171,36 @@ namespace Player instance->reset(); instance->poll(); } + + void Ps3Controller::playRumblePattern(RumbleStep *pattern, int steps) + { + for (int i = 0; i < steps; i++) + { + Ps3.setRumble(pattern[i].intensity, pattern[i].duration); + delay(pattern[i].duration); + } + } + + void Ps3Controller::rumbleTask(void *pvParameters) + { + RumbleStep *pattern = (RumbleStep *)pvParameters; + int steps = 4; // example + + playRumblePattern(pattern, steps); + + vTaskDelete(NULL); + } + + void Ps3Controller::triggerRumble(RumbleStep *pattern) + { + xTaskCreatePinnedToCore( + Ps3Controller::rumbleTask, + "RumbleTask", + 2048, + (void *)pattern, + 1, + NULL, + 0); + } } #endif \ No newline at end of file From 03443904baf4956f3b70074760b56719a03e6ddf Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sat, 21 Mar 2026 11:11:04 -0500 Subject: [PATCH 2/4] Complete controller rumble api --- include/player/ps3-controller.h | 9 +++-- include/player/rumble.h | 41 ++++++++++++++++++++++- src/games/phase-evasion/driver.cpp | 2 ++ src/games/phase-evasion/flare-manager.cpp | 1 + src/player/ps3-controller.cpp | 41 +++++++++++++++++------ 5 files changed, 80 insertions(+), 14 deletions(-) diff --git a/include/player/ps3-controller.h b/include/player/ps3-controller.h index 8218a48..5057f96 100644 --- a/include/player/ps3-controller.h +++ b/include/player/ps3-controller.h @@ -48,15 +48,20 @@ namespace Player ::Ps3Controller *controller; static Ps3Controller *instance; - static void playRumblePattern(RumbleStep *pattern, int steps); + static void playRumblePattern(RumblePattern pattern); static void rumbleTask(void *pvParameters); - void triggerRumble(RumbleStep *pattern); + void triggerRumble(const RumblePattern &pattern); void handleOnConnect(); static void onConnect() { if (instance) instance->handleOnConnect(); } + struct RumbleTaskParams + { + Ps3Controller *instance; + const RumblePattern *pattern; + }; }; } #endif \ No newline at end of file diff --git a/include/player/rumble.h b/include/player/rumble.h index 2265e19..72f814d 100644 --- a/include/player/rumble.h +++ b/include/player/rumble.h @@ -1,10 +1,16 @@ #pragma once +#include + +#include "common.h" + namespace Player { enum class RumbleOptions { - DoublePulse + SingleQuickPulse, + DoubleQuickPulse, + DeathPulse }; struct RumbleStep @@ -12,4 +18,37 @@ namespace Player int intensity; int duration; }; + + class RumblePattern + { + public: + template + RumblePattern(const RumbleStep (&steps)[N]) : steps(steps), count(N) {} + + const RumbleStep *getSteps() const { return steps; } + size_t getCount() const { return count; } + + private: + const RumbleStep *steps; + size_t count; + }; + + static const RumbleStep singleQuickSteps[] = { + {200, 100}}; + static const RumbleStep doubleQuickSteps[] = { + {200, 100}, + {0, 100}, + {200, 100}}; + static const RumbleStep deathSteps[] = { + {255, 100}, + {0, 100}, + {255, 400}, + {0, 100}, + {255, 100}, + {0, 100}, + {255, 400}}; + static const RumblePattern singleQuickRumbleSeq{singleQuickSteps}; + static const RumblePattern doubleQuickRumbleSeq{doubleQuickSteps}; + static const RumblePattern deathRumbleSeq{deathSteps}; + } \ No newline at end of file diff --git a/src/games/phase-evasion/driver.cpp b/src/games/phase-evasion/driver.cpp index a8ec1be..96f604b 100644 --- a/src/games/phase-evasion/driver.cpp +++ b/src/games/phase-evasion/driver.cpp @@ -129,6 +129,7 @@ namespace Games::PhaseEvasion flare.impacted = true; state.current = Actions::MuzzleFlash; gameOverPhaseShift = static_cast(((SystemCore::Configuration::numLeds() / 2) + player.getPosition()) % SystemCore::Configuration::numLeds()); + contextManager->controller.rumble(::Player::RumbleOptions::DeathPulse); wait(20); } } @@ -158,6 +159,7 @@ namespace Games::PhaseEvasion gem.capture(); contextManager->stateManager.getPhaseEvasionGameState().gemsCaptured++; contextManager->stateManager.displayShouldUpdate = true; + contextManager->controller.rumble(::Player::RumbleOptions::DoubleQuickPulse); gem.wait(gemRespawnDelay); } } diff --git a/src/games/phase-evasion/flare-manager.cpp b/src/games/phase-evasion/flare-manager.cpp index 6aca3a9..5875544 100644 --- a/src/games/phase-evasion/flare-manager.cpp +++ b/src/games/phase-evasion/flare-manager.cpp @@ -34,6 +34,7 @@ namespace Games::PhaseEvasion { contextManager->stateManager.getPhaseEvasionGameState().flaresEvaded++; contextManager->stateManager.displayShouldUpdate = true; + contextManager->controller.rumble(Player::RumbleOptions::SingleQuickPulse); flare.completedCycle = false; } } diff --git a/src/player/ps3-controller.cpp b/src/player/ps3-controller.cpp index 8b13fa2..ecc672c 100644 --- a/src/player/ps3-controller.cpp +++ b/src/player/ps3-controller.cpp @@ -43,13 +43,24 @@ namespace Player void Ps3Controller::rumble(RumbleOptions option) { + const RumblePattern *pattern = nullptr; + switch (option) { - case RumbleOptions::DoublePulse: + case RumbleOptions::DoubleQuickPulse: + pattern = &doubleQuickRumbleSeq; + break; + case RumbleOptions::SingleQuickPulse: + pattern = &singleQuickRumbleSeq; + break; + case RumbleOptions::DeathPulse: + pattern = &deathRumbleSeq; break; default: break; } + + triggerRumble(*pattern); } const uint8_t Ps3Controller::rawButtonState(const ControllerButton button) const @@ -172,32 +183,40 @@ namespace Player instance->poll(); } - void Ps3Controller::playRumblePattern(RumbleStep *pattern, int steps) + void Ps3Controller::playRumblePattern(RumblePattern pattern) { - for (int i = 0; i < steps; i++) + const RumbleStep *steps = pattern.getSteps(); + size_t count = pattern.getCount(); + + for (size_t i = 0; i < count; i++) { - Ps3.setRumble(pattern[i].intensity, pattern[i].duration); - delay(pattern[i].duration); + Ps3.setRumble(steps[i].intensity, steps[i].duration); + vTaskDelay(pdMS_TO_TICKS(steps[i].duration)); } } void Ps3Controller::rumbleTask(void *pvParameters) { - RumbleStep *pattern = (RumbleStep *)pvParameters; - int steps = 4; // example + auto *params = (RumbleTaskParams *)pvParameters; + + Ps3Controller *self = params->instance; + const RumblePattern *pattern = params->pattern; - playRumblePattern(pattern, steps); + self->playRumblePattern(*pattern); + delete params; vTaskDelete(NULL); } - - void Ps3Controller::triggerRumble(RumbleStep *pattern) + void Ps3Controller::triggerRumble(const RumblePattern &pattern) { + auto *params = new RumbleTaskParams{ + this, + &pattern}; xTaskCreatePinnedToCore( Ps3Controller::rumbleTask, "RumbleTask", 2048, - (void *)pattern, + (void *)params, 1, NULL, 0); From 5efb58ee6357d8df290bcd15ac104734876e4607 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sat, 21 Mar 2026 11:23:25 -0500 Subject: [PATCH 3/4] Add rumble to all supported games --- include/player/rumble.h | 2 +- src/games/demo/driver.cpp | 1 + src/games/recall/driver.cpp | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/player/rumble.h b/include/player/rumble.h index 72f814d..0eb11b8 100644 --- a/include/player/rumble.h +++ b/include/player/rumble.h @@ -36,7 +36,7 @@ namespace Player static const RumbleStep singleQuickSteps[] = { {200, 100}}; static const RumbleStep doubleQuickSteps[] = { - {200, 100}, + {255, 100}, {0, 100}, {200, 100}}; static const RumbleStep deathSteps[] = { diff --git a/src/games/demo/driver.cpp b/src/games/demo/driver.cpp index ec8f0d4..0832919 100644 --- a/src/games/demo/driver.cpp +++ b/src/games/demo/driver.cpp @@ -10,6 +10,7 @@ namespace Games::Demo { incrementCurrentScore(); contextManager->stateManager.displayShouldUpdate = true; + contextManager->controller.rumble(::Player::RumbleOptions::SingleQuickPulse); logf("Current score: %d", getCurrentScore()); } auto leftInput = contextManager->controller.leftAnalog(); diff --git a/src/games/recall/driver.cpp b/src/games/recall/driver.cpp index 249ea74..314b1ca 100644 --- a/src/games/recall/driver.cpp +++ b/src/games/recall/driver.cpp @@ -115,6 +115,7 @@ namespace Games::Recall state.current = Actions::PlayerResponseEvaluation; sequenceIndex = 0; contextManager->controller.reset(); + contextManager->controller.rumble(::Player::RumbleOptions::SingleQuickPulse); successFadeawayAnimation = 1; log("Ready for User playback"); return; @@ -136,6 +137,7 @@ namespace Games::Recall state.incrementScore(); contextManager->controller.reset(); + contextManager->controller.rumble(::Player::RumbleOptions::DoubleQuickPulse); wait(gameplaySpeedIlluminated * 2); return; } @@ -160,6 +162,7 @@ namespace Games::Recall } logf("User provided the incorrect answer. Entering game over sequence."); + contextManager->controller.rumble(::Player::RumbleOptions::DeathPulse); state.current = Actions::GameOver; } } From 62e7115fa8f358a3820c8f3d2a51d4074c1194c1 Mon Sep 17 00:00:00 2001 From: Eric McDaniel Date: Sat, 21 Mar 2026 11:49:06 -0500 Subject: [PATCH 4/4] Add rumble to base Controller class for future PS4 support --- include/player/controller.h | 3 +++ include/player/ps3-controller.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/player/controller.h b/include/player/controller.h index 755db95..40cad52 100644 --- a/include/player/controller.h +++ b/include/player/controller.h @@ -4,6 +4,7 @@ #include #include "player/controller-properties.h" +#include "player/rumble.h" namespace Player { @@ -33,6 +34,8 @@ namespace Player virtual AnalogStick leftAnalog() = 0; virtual AnalogStick rightAnalog() = 0; + virtual void rumble(RumbleOptions option) = 0; + virtual const uint8_t rawButtonState(const ControllerButton button) const = 0; virtual const bool wasPressed(const ControllerButton button) const = 0; virtual const bool wasPressedAndReleased(const ControllerButton button) const = 0; diff --git a/include/player/ps3-controller.h b/include/player/ps3-controller.h index 5057f96..e59ee75 100644 --- a/include/player/ps3-controller.h +++ b/include/player/ps3-controller.h @@ -35,7 +35,7 @@ namespace Player AnalogStick leftAnalog() override; AnalogStick rightAnalog() override; - void rumble(RumbleOptions option); + void rumble(RumbleOptions option) override; const uint8_t rawButtonState(const ControllerButton button) const; const bool wasPressed(const ControllerButton button) const;