From e107e8c90729405b970351fe3589b3c33add924d Mon Sep 17 00:00:00 2001 From: Xun Zhang Date: Fri, 26 Jan 2024 23:26:38 -0800 Subject: [PATCH 1/2] Refactor --- ESP32-S3-Analog/ESP32-S3-Analog.ino | 179 ++---------------- ESP32-S3-Analog/adjustable.h | 122 ++++++++++++ .../{Joystick_ESP32S2.cpp => joystick.cpp} | 2 +- .../{Joystick_ESP32S2.h => joystick.h} | 0 ESP32-S3-Analog/params.h | 72 +++++++ ESP32-S3-Analog/two_players.h | 110 +++++++++++ 6 files changed, 324 insertions(+), 161 deletions(-) create mode 100644 ESP32-S3-Analog/adjustable.h rename ESP32-S3-Analog/{Joystick_ESP32S2.cpp => joystick.cpp} (99%) rename ESP32-S3-Analog/{Joystick_ESP32S2.h => joystick.h} (100%) create mode 100644 ESP32-S3-Analog/params.h create mode 100644 ESP32-S3-Analog/two_players.h diff --git a/ESP32-S3-Analog/ESP32-S3-Analog.ino b/ESP32-S3-Analog/ESP32-S3-Analog.ino index fe3001f..7d19049 100644 --- a/ESP32-S3-Analog/ESP32-S3-Analog.ino +++ b/ESP32-S3-Analog/ESP32-S3-Analog.ino @@ -1,166 +1,25 @@ -// Enable this mode to pass raw analog data to the game without any post- -// processing. -// The game has a built-in mechanism to calculate which sensor is triggered -// and the force of the hit, so it's recommended to enable this mode. -// This also the provides the most similar experience with the arcade. -// If turned on, the microcontroller will ony do a fast sigle-pass convolution -// over the piezoelectric sensors' inputs, and then pass the data to the game -// directly. -#define RAW_ANALOG_MODE 1 +/******************************************************** -#define PLAYERS 2 -#define CHANNELS 4 +ESP32 is not fast enough to process 16 channels of input +simultaneously, so you can choose one of the 2 modes: -// SAMPLE_CACHE_LENGTH must be power of 2 (8, 16, 32, etc.) -// See cache.h for implementation -#define SAMPLE_CACHE_LENGTH 16 +- MODE_TWO_PLAYERS: Allows the controller to connect 2 + drums, but the sensitivity values are flashed to the + board so you can't adjust them without re-flash the + firmware. If you use the provided board, connect the + 2 drums and ignore the adjustable resistors. -// The maximum value of a hit (not the minumum value to trigger a heavy hit) -// To configure the light and heavy thresholds, do it in the game settings -#define MAX_THRES 5000 +- MODE_ADJUSTABLE: Allows you to adjust the sensitivity + values with the potentiometer as you play, but only + the P1 side of the board will be enabled (you can + configurate it as 1P or 2P in Params.h file). -#if !RAW_ANALOG_MODE -// The minimum value to trigger a light hit -#define HIT_THRES 1000 +**********************************************************/ -// If the reset time is too short, the game may not be able to -// receive the input. From testing I found 40 seems to be the -// minimum value so that the game won't miss any hit. If the game -// occassionally miss the drum input, increase this value -#define RESET_TIME 40 +#define MODE_TWO_PLAYERS + +#ifdef MODE_TWO_PLAYERS +#include "two_players.h" +#elif defined(MODE_ADJUSTABLE) +#include "adjustable.h" #endif - -// Sensitivity multipliers for each channel, 1.0 as the baseline -#define P1_L_DON_SENS 1.0 -#define P1_L_KAT_SENS 1.0 -#define P1_R_DON_SENS 1.0 -#define P1_R_KAT_SENS 1.0 -#define P2_L_DON_SENS 1.0 -#define P2_L_KAT_SENS 1.0 -#define P2_R_DON_SENS 1.0 -#define P2_R_KAT_SENS 1.0 - -// Input pins for each channel -#define P1_L_DON_IN 4 -#define P1_L_KAT_IN 5 -#define P1_R_DON_IN 6 -#define P1_R_KAT_IN 7 -#define P2_L_DON_IN 8 -#define P2_L_KAT_IN 1 -#define P2_R_DON_IN 9 -#define P2_R_KAT_IN 10 - -#define AXIS_RANGE 1023 - -#include "USB.h" -#include "Joystick_ESP32S2.h" -#include "cache.h" - -const byte inPins[PLAYERS][CHANNELS] = { - P1_L_DON_IN, P1_L_KAT_IN, P1_R_DON_IN, P1_R_KAT_IN, - P2_L_DON_IN, P2_L_KAT_IN, P2_R_DON_IN, P2_R_KAT_IN -}; - -const float sensitivities[PLAYERS][CHANNELS] = { - P1_L_DON_SENS, P1_L_KAT_SENS, P1_R_DON_SENS, P1_R_KAT_SENS, - P2_L_DON_SENS, P2_L_KAT_SENS, P2_R_DON_SENS, P2_R_KAT_SENS -}; - -Cache inputWindow[PLAYERS][CHANNELS]; -unsigned long power[PLAYERS][CHANNELS]; - -#if !RAW_ANALOG_MODE -unsigned long lastPower[PLAYERS][CHANNELS]; -bool triggered[PLAYERS]; -unsigned long triggeredTime[PLAYERS][CHANNELS]; -int outputValue[PLAYERS] = {0, 0}; -uint resetTimer[PLAYERS] = {0, 0}; -short maxIndex[PLAYERS] = {0, 0}; -float maxPower[PLAYERS] = {0, 0}; -#endif - -uint axisValues[PLAYERS][CHANNELS] = {0, 0, 0, 0, 0, 0, 0, 0}; - -Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4, - true, true, false, true, true, false, - false, false, false, false, false); - -void setup() { - for (byte p = 0; p < PLAYERS; p++) { - for (byte i = 0; i < CHANNELS; i++) { - power[p][i] = 0; -#if !RAW_ANALOG_MODE - lastPower[p][i] = 0; - triggered[p] = false; -#endif - pinMode(inPins[p][i], INPUT); - } -#if !RAW_ANALOG_MODE - maxIndex[p] = -1; - maxPower[p] = 0; -#endif - } - USB.PID(0x4869); - USB.VID(0x4869); - USB.productName("Taiko Controller"); - USB.manufacturerName("GitHub Community"); - USB.begin(); - Joystick.begin(false); - Joystick.setXAxisRange(-AXIS_RANGE, AXIS_RANGE); - Joystick.setYAxisRange(-AXIS_RANGE, AXIS_RANGE); - Joystick.setRxAxisRange(-AXIS_RANGE, AXIS_RANGE); - Joystick.setRyAxisRange(-AXIS_RANGE, AXIS_RANGE); -} - -void loop() { - - for (byte p = 0; p < PLAYERS; p++) { - - for (byte i = 0; i < CHANNELS; i++) { - inputWindow[p][i].put(analogRead(inPins[p][i])); - power[p][i] = power[p][i] - inputWindow[p][i].get(1) + inputWindow[p][i].get(); -#if !RAW_ANALOG_MODE - if (lastPower[p][i] > maxPower[p] && power[p][i] < lastPower[p][i]) { - maxPower[p] = lastPower[p][i]; - maxIndex[p] = i; - } - lastPower[p][i] = power[p][i]; -#else - float v = power[p][i] * sensitivities[p][i]; - axisValues[p][i] = AXIS_RANGE * (v >= MAX_THRES ? 1 : (v / MAX_THRES)); -#endif - } -#if !RAW_ANALOG_MODE - if (!triggered[p] && maxPower[p] >= HIT_THRES) { - triggered[p] = true; - outputValue[p] = (int)(AXIS_RANGE * (maxPower[p] >= MAX_THRES ? 1 : maxPower[p] / MAX_THRES)); - } - - if (triggered[p] && resetTimer[p] >= RESET_TIME) { - triggered[p] = false; - resetTimer[p] = 0; - maxPower[p] = 0; - maxIndex[p] = -1; - outputValue[p] = 0; - } - - for (byte i = 0; i < CHANNELS; i++) { - if (triggered[p] && i == maxIndex[p]) { - axisValues[p][i] = outputValue[p]; - } else { - axisValues[p][i] = 0; - } - } - - if (triggered[p]) { - resetTimer[p]++; - } -#endif - } - - Joystick.setXAxis(axisValues[0][0] > axisValues[0][1] ? axisValues[0][0] : -axisValues[0][1]); - Joystick.setYAxis(axisValues[0][2] > axisValues[0][3] ? axisValues[0][2] : -axisValues[0][3]); - Joystick.setRxAxis(axisValues[1][0] > axisValues[1][1] ? axisValues[1][0] : -axisValues[1][1]); - Joystick.setRyAxis(axisValues[1][2] > axisValues[1][3] ? axisValues[1][2] : -axisValues[1][3]); - Joystick.sendState(); -} diff --git a/ESP32-S3-Analog/adjustable.h b/ESP32-S3-Analog/adjustable.h new file mode 100644 index 0000000..3c7fd2a --- /dev/null +++ b/ESP32-S3-Analog/adjustable.h @@ -0,0 +1,122 @@ +#include "params.h" + +const byte inPins[CHANNELS] = { + P1_L_DON_IN, P1_L_KAT_IN, P1_R_DON_IN, P1_R_KAT_IN +}; +const byte sensitivityPins[CHANNELS] = { + P1_L_DON_SENS, P1_L_KAT_SENS, P1_R_DON_SENS, P1_R_KAT_SENS +}; + +Cache inputWindow[CHANNELS]; +unsigned long power[CHANNELS]; + +#ifndef RAW_ANALOG_MODE +unsigned long lastPower[PLAYERS][CHANNELS]; +bool triggered[PLAYERS]; +unsigned long triggeredTime[PLAYERS][CHANNELS]; +int outputValue[PLAYERS] = {0, 0}; +uint resetTimer[PLAYERS] = {0, 0}; +short maxIndex[PLAYERS] = {0, 0}; +float maxPower[PLAYERS] = {0, 0}; +#endif + +uint axisValues[CHANNELS] = {0, 0, 0, 0}; + +Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4, + true, true, false, true, true, false, + false, false, false, false, false); + +void setup() { + for (byte i = 0; i < CHANNELS; i++) { + power[i] = 0; +#ifndef RAW_ANALOG_MODE + lastPower[i] = 0; + triggered = false; +#endif + pinMode(inPins[i], INPUT); + pinMode(sensitivityPins[i], INPUT); + } +#ifndef RAW_ANALOG_MODE + maxIndex = -1; + maxPower = 0; +#endif + USB.PID(0x4869); + USB.VID(0x4869); + USB.productName("Taiko Controller"); + USB.manufacturerName("GitHub Community"); + USB.begin(); + Joystick.begin(false); + Joystick.setXAxisRange(-AXIS_RANGE, AXIS_RANGE); + Joystick.setYAxisRange(-AXIS_RANGE, AXIS_RANGE); + Joystick.setRxAxisRange(-AXIS_RANGE, AXIS_RANGE); + Joystick.setRyAxisRange(-AXIS_RANGE, AXIS_RANGE); +} + +void loop() { + + for (byte i = 0; i < CHANNELS; i++) { + inputWindow[i].put(analogRead(inPins[i])); + power[i] = power[i] - inputWindow[i].get(1) + inputWindow[i].get(); +#ifndef RAW_ANALOG_MODE + if (lastPower[i] > maxPower && power[i] < lastPower[i]) { + maxPower = lastPower[i]; + maxIndex = i; + } + lastPower[i] = power[i]; +#else + float x = analogRead(sensitivityPins[i]) / 2048.0 - 1; + float x2 = x * x; + float x3 = x2 * x; + float x4 = x3 * x; + float v = (1.0 + x + 0.5 * x2 + 0.166667 * x3) * power[i]; + axisValues[i] = AXIS_RANGE * (v >= MAX_THRES ? 1 : (v / MAX_THRES)); +#endif + } +#ifndef RAW_ANALOG_MODE + if (!triggered && maxPower >= HIT_THRES) { + triggered = true; + digitalWrite(outPins[maxIndex], HIGH); + outputValue = (int)(AXIS_RANGE * (maxPower >= MAX_THRES ? 1 : maxPower / MAX_THRES)); + } + + if (triggered && resetTimer >= RESET_TIME) { + triggered = false; + resetTimer = 0; + digitalWrite(outPins[maxIndex], LOW); + maxPower = 0; + maxIndex = -1; + outputValue = 0; + } + + for (byte i = 0; i < CHANNELS; i++) { + if (triggered && i == maxIndex) { + axisValues[i] = outputValue; + } else { + axisValues[i] = 0; + } + } + + if (triggered) { + resetTimer++; + } +#endif + +#if PLAYER_SELECT == 1 + Joystick.setXAxis(axisValues[0] > axisValues[1] ? axisValues[0] : -axisValues[1]); + Joystick.setYAxis(axisValues[2] > axisValues[3] ? axisValues[2] : -axisValues[3]); + Joystick.setRxAxis(0); + Joystick.setRyAxis(0); +#elif PLAYER_SELECT == 2 + Joystick.setXAxis(0); + Joystick.setYAxis(0); + Joystick.setRxAxis(axisValues[0] > axisValues[1] ? axisValues[0] : -axisValues[1]); + Joystick.setRyAxis(axisValues[2] > axisValues[3] ? axisValues[2] : -axisValues[3]); +#else + Joystick.setXAxis(0); + Joystick.setYAxis(0); + Joystick.setRxAxis(0); + Joystick.setRyAxis(0); +#endif + + Joystick.sendState(); +} diff --git a/ESP32-S3-Analog/Joystick_ESP32S2.cpp b/ESP32-S3-Analog/joystick.cpp similarity index 99% rename from ESP32-S3-Analog/Joystick_ESP32S2.cpp rename to ESP32-S3-Analog/joystick.cpp index ab5db3c..12bd93d 100644 --- a/ESP32-S3-Analog/Joystick_ESP32S2.cpp +++ b/ESP32-S3-Analog/joystick.cpp @@ -20,7 +20,7 @@ Edited by Schnoog to make it running on ESP32-S2/s3 devices */ -#include "Joystick_ESP32S2.h" +#include "joystick.h" #define JOYSTICK_REPORT_ID_INDEX 7 #define JOYSTICK_AXIS_MINIMUM 0 diff --git a/ESP32-S3-Analog/Joystick_ESP32S2.h b/ESP32-S3-Analog/joystick.h similarity index 100% rename from ESP32-S3-Analog/Joystick_ESP32S2.h rename to ESP32-S3-Analog/joystick.h diff --git a/ESP32-S3-Analog/params.h b/ESP32-S3-Analog/params.h new file mode 100644 index 0000000..caf38e0 --- /dev/null +++ b/ESP32-S3-Analog/params.h @@ -0,0 +1,72 @@ +// Enable this mode to pass raw analog data to the game without any post- +// processing. +// The game has a built-in mechanism to calculate which sensor is triggered +// and the force of the hit, so it's recommended to enable this mode. +// This also the provides the most similar experience with the arcade. +// To disable this mode, remove or comment out this line +#define RAW_ANALOG_MODE + +// For MODE_ADJUSTABLE +// Select which player this board is. 1 for P1 and 2 for P2. +#define PLAYER_SELECT 1 + +// For MODE_TWO_PLAYERS +// Sensitivity multipliers for each channel, 1.0 as the baseline. +#define P1_L_DON_SENS 1.0 +#define P1_L_KAT_SENS 1.0 +#define P1_R_DON_SENS 1.0 +#define P1_R_KAT_SENS 1.0 +#define P2_L_DON_SENS 1.0 +#define P2_L_KAT_SENS 1.0 +#define P2_R_DON_SENS 1.0 +#define P2_R_KAT_SENS 1.0 + +/******************************************************** + CHANGING THE FOLLOWING PARAMETERS ARE NOT RECOMMENDED + UNLESS YOU KNOW HOW THEY WORKS +**********************************************************/ + +// Cache length must be the power of 2 (8, 16, 32, etc.) +// See cache.h for the reason +#define SAMPLE_CACHE_LENGTH 32 + +// The maximum value of a hit (not the minumum value to trigger a heavy hit) +// To configure the light and heavy thresholds, do it in the game settings +#define MAX_THRES 5000 + +// Input pins for each channel +#define P1_L_DON_IN 4 +#define P1_L_KAT_IN 5 +#define P1_R_DON_IN 6 +#define P1_R_KAT_IN 7 + +#define P2_L_DON_IN 8 +#define P2_L_KAT_IN 1 +#define P2_R_DON_IN 9 +#define P2_R_KAT_IN 10 + +// Sensitivity adjustment potentiometer input pins +#define P1_L_DON_SENS 15 +#define P1_L_KAT_SENS 16 +#define P1_R_DON_SENS 17 +#define P1_R_KAT_SENS 18 + +#define AXIS_RANGE 1023 + +#define PLAYERS 2 +#define CHANNELS 4 + +#ifndef RAW_ANALOG_MODE +// The minimum value to trigger a light hit +#define HIT_THRES 1000 + +// If the reset time is too short, the game may not be able to +// receive the input. From testing I found 40 seems to be the +// minimum value so that the game won't miss any hit. If the game +// occassionally miss the drum input, increase this value +#define RESET_TIME 40 +#endif + +#include +#include "joystick.h" +#include "cache.h" \ No newline at end of file diff --git a/ESP32-S3-Analog/two_players.h b/ESP32-S3-Analog/two_players.h new file mode 100644 index 0000000..adf4526 --- /dev/null +++ b/ESP32-S3-Analog/two_players.h @@ -0,0 +1,110 @@ +#include "params.h" + +const byte inPins[PLAYERS][CHANNELS] = { + P1_L_DON_IN, P1_L_KAT_IN, P1_R_DON_IN, P1_R_KAT_IN, + P2_L_DON_IN, P2_L_KAT_IN, P2_R_DON_IN, P2_R_KAT_IN +}; + +const float sensitivities[PLAYERS][CHANNELS] = { + P1_L_DON_SENS, P1_L_KAT_SENS, P1_R_DON_SENS, P1_R_KAT_SENS, + P2_L_DON_SENS, P2_L_KAT_SENS, P2_R_DON_SENS, P2_R_KAT_SENS +}; + +Cache inputWindow[PLAYERS][CHANNELS]; +unsigned long power[PLAYERS][CHANNELS]; + +#ifndef RAW_ANALOG_MODE +unsigned long lastPower[PLAYERS][CHANNELS]; +bool triggered[PLAYERS]; +unsigned long triggeredTime[PLAYERS][CHANNELS]; +int outputValue[PLAYERS] = {0, 0}; +uint resetTimer[PLAYERS] = {0, 0}; +short maxIndex[PLAYERS] = {0, 0}; +float maxPower[PLAYERS] = {0, 0}; +#endif + +uint axisValues[PLAYERS][CHANNELS] = {0, 0, 0, 0, 0, 0, 0, 0}; + +Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4, + true, true, false, true, true, false, + false, false, false, false, false); + +void setup() { + for (byte p = 0; p < PLAYERS; p++) { + for (byte i = 0; i < CHANNELS; i++) { + power[p][i] = 0; +#ifndef RAW_ANALOG_MODE + lastPower[p][i] = 0; + triggered[p] = false; +#endif + pinMode(inPins[p][i], INPUT); + } +#ifndef RAW_ANALOG_MODE + maxIndex[p] = -1; + maxPower[p] = 0; +#endif + } + USB.PID(0x4869); + USB.VID(0x4869); + USB.productName("Taiko Controller"); + USB.manufacturerName("GitHub Community"); + USB.begin(); + Joystick.begin(false); + Joystick.setXAxisRange(-AXIS_RANGE, AXIS_RANGE); + Joystick.setYAxisRange(-AXIS_RANGE, AXIS_RANGE); + Joystick.setRxAxisRange(-AXIS_RANGE, AXIS_RANGE); + Joystick.setRyAxisRange(-AXIS_RANGE, AXIS_RANGE); +} + +void loop() { + + for (byte p = 0; p < PLAYERS; p++) { + + for (byte i = 0; i < CHANNELS; i++) { + inputWindow[p][i].put(analogRead(inPins[p][i])); + power[p][i] = power[p][i] - inputWindow[p][i].get(1) + inputWindow[p][i].get(); +#ifndef RAW_ANALOG_MODE + if (lastPower[p][i] > maxPower[p] && power[p][i] < lastPower[p][i]) { + maxPower[p] = lastPower[p][i]; + maxIndex[p] = i; + } + lastPower[p][i] = power[p][i]; +#else + float v = power[p][i] * sensitivities[p][i]; + axisValues[p][i] = AXIS_RANGE * (v >= MAX_THRES ? 1 : (v / MAX_THRES)); +#endif + } +#ifndef RAW_ANALOG_MODE + if (!triggered[p] && maxPower[p] >= HIT_THRES) { + triggered[p] = true; + outputValue[p] = (int)(AXIS_RANGE * (maxPower[p] >= MAX_THRES ? 1 : maxPower[p] / MAX_THRES)); + } + + if (triggered[p] && resetTimer[p] >= RESET_TIME) { + triggered[p] = false; + resetTimer[p] = 0; + maxPower[p] = 0; + maxIndex[p] = -1; + outputValue[p] = 0; + } + + for (byte i = 0; i < CHANNELS; i++) { + if (triggered[p] && i == maxIndex[p]) { + axisValues[p][i] = outputValue[p]; + } else { + axisValues[p][i] = 0; + } + } + + if (triggered[p]) { + resetTimer[p]++; + } +#endif + } + + Joystick.setXAxis(axisValues[0][0] > axisValues[0][1] ? axisValues[0][0] : -axisValues[0][1]); + Joystick.setYAxis(axisValues[0][2] > axisValues[0][3] ? axisValues[0][2] : -axisValues[0][3]); + Joystick.setRxAxis(axisValues[1][0] > axisValues[1][1] ? axisValues[1][0] : -axisValues[1][1]); + Joystick.setRyAxis(axisValues[1][2] > axisValues[1][3] ? axisValues[1][2] : -axisValues[1][3]); + Joystick.sendState(); +} From eb0e5302a9ab6ccecec80e6c3b6b14a53afd74db Mon Sep 17 00:00:00 2001 From: ShikyC Date: Mon, 29 Jan 2024 00:29:07 -0800 Subject: [PATCH 2/2] Bugfix --- ESP32-S3-Analog/adjustable.h | 2 +- ESP32-S3-Analog/params.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ESP32-S3-Analog/adjustable.h b/ESP32-S3-Analog/adjustable.h index 3c7fd2a..69d0682 100644 --- a/ESP32-S3-Analog/adjustable.h +++ b/ESP32-S3-Analog/adjustable.h @@ -4,7 +4,7 @@ const byte inPins[CHANNELS] = { P1_L_DON_IN, P1_L_KAT_IN, P1_R_DON_IN, P1_R_KAT_IN }; const byte sensitivityPins[CHANNELS] = { - P1_L_DON_SENS, P1_L_KAT_SENS, P1_R_DON_SENS, P1_R_KAT_SENS + P1_L_DON_SENS_IN, P1_L_KAT_SENS_IN, P1_R_DON_SENS_IN, P1_R_KAT_SENS_IN }; Cache inputWindow[CHANNELS]; diff --git a/ESP32-S3-Analog/params.h b/ESP32-S3-Analog/params.h index caf38e0..82d0dc0 100644 --- a/ESP32-S3-Analog/params.h +++ b/ESP32-S3-Analog/params.h @@ -46,10 +46,10 @@ #define P2_R_KAT_IN 10 // Sensitivity adjustment potentiometer input pins -#define P1_L_DON_SENS 15 -#define P1_L_KAT_SENS 16 -#define P1_R_DON_SENS 17 -#define P1_R_KAT_SENS 18 +#define P1_L_DON_SENS_IN 15 +#define P1_L_KAT_SENS_IN 16 +#define P1_R_DON_SENS_IN 17 +#define P1_R_KAT_SENS_IN 18 #define AXIS_RANGE 1023