diff --git a/ESP32-S3-Analog/ESP32-S3-Analog.ino b/ESP32-S3-Analog/ESP32-S3-Analog.ino new file mode 100644 index 0000000..692d3bc --- /dev/null +++ b/ESP32-S3-Analog/ESP32-S3-Analog.ino @@ -0,0 +1,136 @@ +#define CHANNELS 4 + +// SAMPLE_CACHE_LENGTH must be power of 2 (8, 16, 32, etc.) +// See cache.h for implementation +#define SAMPLE_CACHE_LENGTH 16 + +// The thresholds are also dependent on SAMPLE_CACHE_LENGTH, if you +// changed SAMPLE_CACHE_LENGTH, you should also adjust thresholds +#define MAX_THRES 5000 +#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 + +// Sensitivity multipliers for each channel, 1.0 as the baseline +#define L_DON_SENS 1.0 +#define L_KAT_SENS 1.0 +#define R_DON_SENS 1.0 +#define R_KAT_SENS 1.0 + +// Input pins for each channel +#define L_DON_IN 4 +#define L_KAT_IN 5 +#define R_DON_IN 6 +#define R_KAT_IN 7 + +// Output LED pins for each channel (just for visualization) +#define L_DON_LED 10 +#define L_KAT_LED 11 +#define R_DON_LED 12 +#define R_KAT_LED 13 + +#include "USB.h" +#include "Joystick_ESP32S2.h" +#include "cache.h" + +Cache inputWindow[CHANNELS]; +unsigned long power[CHANNELS]; +unsigned long lastPower[CHANNELS]; + +bool triggered; +unsigned long triggeredTime[CHANNELS]; + +const byte inPins[] = {L_DON_IN, L_KAT_IN, R_DON_IN, R_KAT_IN}; +const byte outPins[] = {L_DON_LED, L_KAT_LED, R_DON_LED, R_KAT_LED}; +const float sensitivities[] = {L_DON_SENS, L_KAT_SENS, R_DON_SENS, R_KAT_SENS}; +uint axisValues[] = {0, 0, 0, 0}; + +uint shifter = 0; +int outputValue = 0; +uint resetTimer = 0; + +short maxIndex; +float maxPower; + +unsigned long lastTime; + +Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4, + true, true, true, true, true, true, + false, false, false, false, false); + +void setup() { + Serial.begin(250000); + for (byte i = 0; i < CHANNELS; i++) { + power[i] = 0; + lastPower[i] = 0; + triggered = false; + pinMode(inPins[i], INPUT); + pinMode(outPins[i], OUTPUT); + } + maxIndex = -1; + maxPower = 0; + lastTime = micros(); + USB.PID(0x4869); + USB.VID(0x4869); + USB.productName("Taiko Controller"); + USB.manufacturerName("GitHub Community"); + USB.begin(); + Joystick.begin(false); + Joystick.setXAxisRange(-1024, 1023); + Joystick.setYAxisRange(-1024, 1023); + Joystick.setZAxisRange(-1024, 1023); + Joystick.setRxAxisRange(-1024, 1023); + Joystick.setRyAxisRange(-1024, 1023); + Joystick.setRzAxisRange(-1024, 1023); +} + +void loop() { + + for (byte i = 0; i < CHANNELS; i++) { + inputWindow[i].put(analogRead(inPins[i])); + power[i] = sensitivities[i] * (power[i] - inputWindow[i].get(1) + inputWindow[i].get()); + + if (lastPower[i] > maxPower && power[i] < lastPower[i]) { + maxPower = lastPower[i]; + maxIndex = i; + } + lastPower[i] = power[i]; + } + + if (!triggered && maxPower >= HIT_THRES) { + triggered = true; + digitalWrite(outPins[maxIndex], HIGH); + outputValue = (int)(1023 * (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; + } + } + + Joystick.setXAxis(axisValues[0]); + Joystick.setYAxis(axisValues[1]); + Joystick.setRxAxis(axisValues[2]); + Joystick.setRyAxis(axisValues[3]); + Joystick.sendState(); + + if (triggered) { + resetTimer++; + } +} diff --git a/ESP32-S3-Analog/Joystick_ESP32S2.cpp b/ESP32-S3-Analog/Joystick_ESP32S2.cpp new file mode 100644 index 0000000..ab5db3c --- /dev/null +++ b/ESP32-S3-Analog/Joystick_ESP32S2.cpp @@ -0,0 +1,694 @@ +/* + Joystick_ESP32S2.cpp + + Copyright (c) 2015-2017, Matthew Heironimus + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Edited by Schnoog to make it running on ESP32-S2/s3 devices +*/ + +#include "Joystick_ESP32S2.h" + +#define JOYSTICK_REPORT_ID_INDEX 7 +#define JOYSTICK_AXIS_MINIMUM 0 +#define JOYSTICK_AXIS_MAXIMUM 65535 +#define JOYSTICK_SIMULATOR_MINIMUM 0 +#define JOYSTICK_SIMULATOR_MAXIMUM 65535 + +#define JOYSTICK_INCLUDE_X_AXIS B00000001 +#define JOYSTICK_INCLUDE_Y_AXIS B00000010 +#define JOYSTICK_INCLUDE_Z_AXIS B00000100 +#define JOYSTICK_INCLUDE_RX_AXIS B00001000 +#define JOYSTICK_INCLUDE_RY_AXIS B00010000 +#define JOYSTICK_INCLUDE_RZ_AXIS B00100000 + +#define JOYSTICK_INCLUDE_RUDDER B00000001 +#define JOYSTICK_INCLUDE_THROTTLE B00000010 +#define JOYSTICK_INCLUDE_ACCELERATOR B00000100 +#define JOYSTICK_INCLUDE_BRAKE B00001000 +#define JOYSTICK_INCLUDE_STEERING B00010000 + + + +Joystick_::Joystick_( + uint8_t hidReportId, + uint8_t joystickType, + uint8_t buttonCount, + uint8_t hatSwitchCount, + bool includeXAxis, + bool includeYAxis, + bool includeZAxis, + bool includeRxAxis, + bool includeRyAxis, + bool includeRzAxis, + bool includeRudder, + bool includeThrottle, + bool includeAccelerator, + bool includeBrake, + bool includeSteering) +{ + // Set the USB HID Report ID + _hidReportId = hidReportId; + + // Save Joystick Settings + _buttonCount = buttonCount; + _hatSwitchCount = hatSwitchCount; + _includeAxisFlags = 0; + _includeAxisFlags |= (includeXAxis ? JOYSTICK_INCLUDE_X_AXIS : 0); + _includeAxisFlags |= (includeYAxis ? JOYSTICK_INCLUDE_Y_AXIS : 0); + _includeAxisFlags |= (includeZAxis ? JOYSTICK_INCLUDE_Z_AXIS : 0); + _includeAxisFlags |= (includeRxAxis ? JOYSTICK_INCLUDE_RX_AXIS : 0); + _includeAxisFlags |= (includeRyAxis ? JOYSTICK_INCLUDE_RY_AXIS : 0); + _includeAxisFlags |= (includeRzAxis ? JOYSTICK_INCLUDE_RZ_AXIS : 0); + _includeSimulatorFlags = 0; + _includeSimulatorFlags |= (includeRudder ? JOYSTICK_INCLUDE_RUDDER : 0); + _includeSimulatorFlags |= (includeThrottle ? JOYSTICK_INCLUDE_THROTTLE : 0); + _includeSimulatorFlags |= (includeAccelerator ? JOYSTICK_INCLUDE_ACCELERATOR : 0); + _includeSimulatorFlags |= (includeBrake ? JOYSTICK_INCLUDE_BRAKE : 0); + _includeSimulatorFlags |= (includeSteering ? JOYSTICK_INCLUDE_STEERING : 0); + + // Build Joystick HID Report Description + // Button Calculations + uint8_t buttonsInLastByte = _buttonCount % 8; + uint8_t buttonPaddingBits = 0; + if (buttonsInLastByte > 0) + { + buttonPaddingBits = 8 - buttonsInLastByte; + } + + // Axis Calculations + uint8_t axisCount = (includeXAxis == true) + + (includeYAxis == true) + + (includeZAxis == true) + + (includeRxAxis == true) + + (includeRyAxis == true) + + (includeRzAxis == true); + + uint8_t simulationCount = (includeRudder == true) + + (includeThrottle == true) + + (includeAccelerator == true) + + (includeBrake == true) + + (includeSteering == true); + + uint8_t tempHidReportDescriptor[150]; + hidReportDescriptorSize = 0; + + // USAGE_PAGE (Generic Desktop) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // USAGE (Joystick - 0x04; Gamepad - 0x05; Multi-axis Controller - 0x08) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = joystickType; + + // COLLECTION (Application) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xa1; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // REPORT_ID (Default: 3) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x85; + tempHidReportDescriptor[hidReportDescriptorSize++] = _hidReportId; + + if (_buttonCount > 0) { + + // USAGE_PAGE (Button) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + + // USAGE_MINIMUM (Button 1) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x19; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // USAGE_MAXIMUM (Button 32) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x29; + tempHidReportDescriptor[hidReportDescriptorSize++] = _buttonCount; + + // LOGICAL_MINIMUM (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // LOGICAL_MAXIMUM (1) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // REPORT_SIZE (1) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // REPORT_COUNT (# of buttons) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = _buttonCount; + + // UNIT_EXPONENT (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x55; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // UNIT (None) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x65; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // INPUT (Data,Var,Abs) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; + + if (buttonPaddingBits > 0) { + + // REPORT_SIZE (1) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // REPORT_COUNT (# of padding bits) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = buttonPaddingBits; + + // INPUT (Const,Var,Abs) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x03; + + } // Padding Bits Needed + + } // Buttons + + if ((axisCount > 0) || (_hatSwitchCount > 0)) { + + // USAGE_PAGE (Generic Desktop) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + } + + if (_hatSwitchCount > 0) { + + // USAGE (Hat Switch) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x39; + + // LOGICAL_MINIMUM (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // LOGICAL_MAXIMUM (7) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x07; + + // PHYSICAL_MINIMUM (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // PHYSICAL_MAXIMUM (315) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x46; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x3B; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // UNIT (Eng Rot:Angular Pos) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x65; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x14; + + // REPORT_SIZE (4) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x04; + + // REPORT_COUNT (1) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // INPUT (Data,Var,Abs) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; + + if (_hatSwitchCount > 1) { + + // USAGE (Hat Switch) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x39; + + // LOGICAL_MINIMUM (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // LOGICAL_MAXIMUM (7) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x07; + + // PHYSICAL_MINIMUM (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // PHYSICAL_MAXIMUM (315) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x46; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x3B; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // UNIT (Eng Rot:Angular Pos) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x65; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x14; + + // REPORT_SIZE (4) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x04; + + // REPORT_COUNT (1) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // INPUT (Data,Var,Abs) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; + + } else { + + // Use Padding Bits + + // REPORT_SIZE (1) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // REPORT_COUNT (4) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x04; + + // INPUT (Const,Var,Abs) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x03; + + } // One or Two Hat Switches? + + } // Hat Switches + + if (axisCount > 0) { + + // USAGE (Pointer) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; + + // LOGICAL_MINIMUM (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // LOGICAL_MAXIMUM (65535) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x27; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0XFF; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0XFF; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // REPORT_SIZE (16) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x10; + + // REPORT_COUNT (axisCount) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = axisCount; + + // COLLECTION (Physical) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + if (includeXAxis == true) { + // USAGE (X) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x30; + } + + if (includeYAxis == true) { + // USAGE (Y) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x31; + } + + if (includeZAxis == true) { + // USAGE (Z) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x32; + } + + if (includeRxAxis == true) { + // USAGE (Rx) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x33; + } + + if (includeRyAxis == true) { + // USAGE (Ry) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x34; + } + + if (includeRzAxis == true) { + // USAGE (Rz) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; + } + + // INPUT (Data,Var,Abs) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; + + // END_COLLECTION (Physical) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; + + } // X, Y, Z, Rx, Ry, and Rz Axis + + if (simulationCount > 0) { + + // USAGE_PAGE (Simulation Controls) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; + + // LOGICAL_MINIMUM (0) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // LOGICAL_MAXIMUM (65535) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x27; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0XFF; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0XFF; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + // REPORT_SIZE (16) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x10; + + // REPORT_COUNT (simulationCount) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; + tempHidReportDescriptor[hidReportDescriptorSize++] = simulationCount; + + // COLLECTION (Physical) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; + + if (includeRudder == true) { + // USAGE (Rudder) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xBA; + } + + if (includeThrottle == true) { + // USAGE (Throttle) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xBB; + } + + if (includeAccelerator == true) { + // USAGE (Accelerator) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xC4; + } + + if (includeBrake == true) { + // USAGE (Brake) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xC5; + } + + if (includeSteering == true) { + // USAGE (Steering) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xC8; + } + + // INPUT (Data,Var,Abs) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; + tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; + + // END_COLLECTION (Physical) + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; + + } // Simulation Controls + + // END_COLLECTION + tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; + + // Create a copy of the HID Report Descriptor template that is just the right size + //org: uint8_t *customHidReportDescriptor = new uint8_t[hidReportDescriptorSize]; + customHidReportDescriptor = new uint8_t[hidReportDescriptorSize]; + memcpy(customHidReportDescriptor, tempHidReportDescriptor, hidReportDescriptorSize); + + // Register HID Report Description + HID.addDevice(this, hidReportDescriptorSize); + // Setup Joystick State + if (buttonCount > 0) { + _buttonValuesArraySize = _buttonCount / 8; + if ((_buttonCount % 8) > 0) { + _buttonValuesArraySize++; + } + _buttonValues = new uint8_t[_buttonValuesArraySize]; + } + + // Calculate HID Report Size + _hidReportSize = _buttonValuesArraySize; + _hidReportSize += (_hatSwitchCount > 0); + _hidReportSize += (axisCount * 2); + _hidReportSize += (simulationCount * 2); + + // Initialize Joystick State + _xAxis = 0; + _yAxis = 0; + _zAxis = 0; + _xAxisRotation = 0; + _yAxisRotation = 0; + _zAxisRotation = 0; + _throttle = 0; + _rudder = 0; + _accelerator = 0; + _brake = 0; + _steering = 0; + for (int index = 0; index < JOYSTICK_HATSWITCH_COUNT_MAXIMUM; index++) + { + _hatSwitchValues[index] = JOYSTICK_HATSWITCH_RELEASE; + } + for (int index = 0; index < _buttonValuesArraySize; index++) + { + _buttonValues[index] = 0; + } + + +} + + + uint16_t Joystick_::_onGetDescriptor(uint8_t* buffer){ + memcpy(buffer, customHidReportDescriptor,hidReportDescriptorSize); + return hidReportDescriptorSize; + } + + +void Joystick_::begin(bool initAutoSendState, uint8_t intervalMs) +{ + HID.begin(); + _autoSendState = initAutoSendState; + sendState(); +} + +void Joystick_::end() +{ +} + +void Joystick_::setButton(uint8_t button, uint8_t value) +{ + if (value == 0) + { + releaseButton(button); + } + else + { + pressButton(button); + } +} +void Joystick_::pressButton(uint8_t button) +{ + if (button >= _buttonCount) return; + + int index = button / 8; + int bit = button % 8; + + bitSet(_buttonValues[index], bit); + if (_autoSendState) sendState(); +} +void Joystick_::releaseButton(uint8_t button) +{ + if (button >= _buttonCount) return; + + int index = button / 8; + int bit = button % 8; + + bitClear(_buttonValues[index], bit); + if (_autoSendState) sendState(); +} + +void Joystick_::setXAxis(int32_t value) +{ + _xAxis = value; + if (_autoSendState) sendState(); +} +void Joystick_::setYAxis(int32_t value) +{ + _yAxis = value; + if (_autoSendState) sendState(); +} +void Joystick_::setZAxis(int32_t value) +{ + _zAxis = value; + if (_autoSendState) sendState(); +} + +void Joystick_::setRxAxis(int32_t value) +{ + _xAxisRotation = value; + if (_autoSendState) sendState(); +} +void Joystick_::setRyAxis(int32_t value) +{ + _yAxisRotation = value; + if (_autoSendState) sendState(); +} +void Joystick_::setRzAxis(int32_t value) +{ + _zAxisRotation = value; + if (_autoSendState) sendState(); +} + +void Joystick_::setRudder(int32_t value) +{ + _rudder = value; + if (_autoSendState) sendState(); +} +void Joystick_::setThrottle(int32_t value) +{ + _throttle = value; + if (_autoSendState) sendState(); +} +void Joystick_::setAccelerator(int32_t value) +{ + _accelerator = value; + if (_autoSendState) sendState(); +} +void Joystick_::setBrake(int32_t value) +{ + _brake = value; + if (_autoSendState) sendState(); +} +void Joystick_::setSteering(int32_t value) +{ + _steering = value; + if (_autoSendState) sendState(); +} + +void Joystick_::setHatSwitch(int8_t hatSwitchIndex, int16_t value) +{ + if (hatSwitchIndex >= _hatSwitchCount) return; + + _hatSwitchValues[hatSwitchIndex] = value; + if (_autoSendState) sendState(); +} + +int Joystick_::buildAndSet16BitValue(bool includeValue, int32_t value, int32_t valueMinimum, int32_t valueMaximum, int32_t actualMinimum, int32_t actualMaximum, uint8_t dataLocation[]) +{ + int32_t convertedValue; + uint8_t highByte; + uint8_t lowByte; + int32_t realMinimum = min(valueMinimum, valueMaximum); + int32_t realMaximum = max(valueMinimum, valueMaximum); + + if (includeValue == false) return 0; + + if (value < realMinimum) { + value = realMinimum; + } + if (value > realMaximum) { + value = realMaximum; + } + + if (valueMinimum > valueMaximum) { + // Values go from a larger number to a smaller number (e.g. 1024 to 0) + value = realMaximum - value + realMinimum; + } + + convertedValue = map(value, realMinimum, realMaximum, actualMinimum, actualMaximum); + + highByte = (uint8_t)(convertedValue >> 8); + lowByte = (uint8_t)(convertedValue & 0x00FF); + + dataLocation[0] = lowByte; + dataLocation[1] = highByte; + + return 2; +} + +int Joystick_::buildAndSetAxisValue(bool includeAxis, int32_t axisValue, int32_t axisMinimum, int32_t axisMaximum, uint8_t dataLocation[]) +{ + return buildAndSet16BitValue(includeAxis, axisValue, axisMinimum, axisMaximum, JOYSTICK_AXIS_MINIMUM, JOYSTICK_AXIS_MAXIMUM, dataLocation); +} + +int Joystick_::buildAndSetSimulationValue(bool includeValue, int32_t value, int32_t valueMinimum, int32_t valueMaximum, uint8_t dataLocation[]) +{ + return buildAndSet16BitValue(includeValue, value, valueMinimum, valueMaximum, JOYSTICK_SIMULATOR_MINIMUM, JOYSTICK_SIMULATOR_MAXIMUM, dataLocation); +} + +void Joystick_::sendState() +{ + uint8_t data[_hidReportSize]; + int index = 0; + + // Load Button State + for (; index < _buttonValuesArraySize; index++) + { + data[index] = _buttonValues[index]; + } + + // Set Hat Switch Values + if (_hatSwitchCount > 0) { + + // Calculate hat-switch values + uint8_t convertedHatSwitch[JOYSTICK_HATSWITCH_COUNT_MAXIMUM]; + for (int hatSwitchIndex = 0; hatSwitchIndex < JOYSTICK_HATSWITCH_COUNT_MAXIMUM; hatSwitchIndex++) + { + if (_hatSwitchValues[hatSwitchIndex] < 0) + { + convertedHatSwitch[hatSwitchIndex] = 8; + } + else + { + convertedHatSwitch[hatSwitchIndex] = (_hatSwitchValues[hatSwitchIndex] % 360) / 45; + } + } + + // Pack hat-switch states into a single byte + data[index++] = (convertedHatSwitch[1] << 4) | (B00001111 & convertedHatSwitch[0]); + + } // Hat Switches + + // Set Axis Values + index += buildAndSetAxisValue(_includeAxisFlags & JOYSTICK_INCLUDE_X_AXIS, _xAxis, _xAxisMinimum, _xAxisMaximum, &(data[index])); + index += buildAndSetAxisValue(_includeAxisFlags & JOYSTICK_INCLUDE_Y_AXIS, _yAxis, _yAxisMinimum, _yAxisMaximum, &(data[index])); + index += buildAndSetAxisValue(_includeAxisFlags & JOYSTICK_INCLUDE_Z_AXIS, _zAxis, _zAxisMinimum, _zAxisMaximum, &(data[index])); + index += buildAndSetAxisValue(_includeAxisFlags & JOYSTICK_INCLUDE_RX_AXIS, _xAxisRotation, _rxAxisMinimum, _rxAxisMaximum, &(data[index])); + index += buildAndSetAxisValue(_includeAxisFlags & JOYSTICK_INCLUDE_RY_AXIS, _yAxisRotation, _ryAxisMinimum, _ryAxisMaximum, &(data[index])); + index += buildAndSetAxisValue(_includeAxisFlags & JOYSTICK_INCLUDE_RZ_AXIS, _zAxisRotation, _rzAxisMinimum, _rzAxisMaximum, &(data[index])); + + // Set Simulation Values + index += buildAndSetSimulationValue(_includeSimulatorFlags & JOYSTICK_INCLUDE_RUDDER, _rudder, _rudderMinimum, _rudderMaximum, &(data[index])); + index += buildAndSetSimulationValue(_includeSimulatorFlags & JOYSTICK_INCLUDE_THROTTLE, _throttle, _throttleMinimum, _throttleMaximum, &(data[index])); + index += buildAndSetSimulationValue(_includeSimulatorFlags & JOYSTICK_INCLUDE_ACCELERATOR, _accelerator, _acceleratorMinimum, _acceleratorMaximum, &(data[index])); + index += buildAndSetSimulationValue(_includeSimulatorFlags & JOYSTICK_INCLUDE_BRAKE, _brake, _brakeMinimum, _brakeMaximum, &(data[index])); + index += buildAndSetSimulationValue(_includeSimulatorFlags & JOYSTICK_INCLUDE_STEERING, _steering, _steeringMinimum, _steeringMaximum, &(data[index])); + + + if (HID.ready()) { + HID.SendReport(_hidReportId, data, sizeof(data),0); + } + +} \ No newline at end of file diff --git a/ESP32-S3-Analog/Joystick_ESP32S2.h b/ESP32-S3-Analog/Joystick_ESP32S2.h new file mode 100644 index 0000000..1a1d159 --- /dev/null +++ b/ESP32-S3-Analog/Joystick_ESP32S2.h @@ -0,0 +1,217 @@ +/* + Joystick.h + + Copyright (c) 2015-2017, Matthew Heironimus + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Edited by Schnoog to make it running on ESP32-S2/s3 devices + +*/ + +#ifndef JOYSTICK_h +#define JOYSTICK_h + +#include +#include +#include "USB.h" +#include "USBHID.h" + + + +//================================================================================ +// Joystick (Gamepad) + +#define JOYSTICK_DEFAULT_REPORT_ID 0x03 +#define JOYSTICK_DEFAULT_BUTTON_COUNT 32 +#define JOYSTICK_DEFAULT_AXIS_MINIMUM 0 +#define JOYSTICK_DEFAULT_AXIS_MAXIMUM 1023 +#define JOYSTICK_DEFAULT_SIMULATOR_MINIMUM 0 +#define JOYSTICK_DEFAULT_SIMULATOR_MAXIMUM 1023 +#define JOYSTICK_DEFAULT_HATSWITCH_COUNT 2 +#define JOYSTICK_HATSWITCH_COUNT_MAXIMUM 2 +#define JOYSTICK_HATSWITCH_RELEASE -1 +#define JOYSTICK_TYPE_JOYSTICK 0x04 +#define JOYSTICK_TYPE_GAMEPAD 0x05 +#define JOYSTICK_TYPE_MULTI_AXIS 0x08 + +class Joystick_: public USBHIDDevice +{ +private: + + // Joystick State + int32_t _xAxis; + int32_t _yAxis; + int32_t _zAxis; + int32_t _xAxisRotation; + int32_t _yAxisRotation; + int32_t _zAxisRotation; + int32_t _throttle; + int32_t _rudder; + int32_t _accelerator; + int32_t _brake; + int32_t _steering; + int16_t _hatSwitchValues[JOYSTICK_HATSWITCH_COUNT_MAXIMUM]; + uint8_t *_buttonValues = NULL; + + // Joystick Settings + bool _autoSendState; + uint8_t _buttonCount; + uint8_t _buttonValuesArraySize = 0; + uint8_t _hatSwitchCount; + uint8_t _includeAxisFlags; + uint8_t _includeSimulatorFlags; + int32_t _xAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; + int32_t _xAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; + int32_t _yAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; + int32_t _yAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; + int32_t _zAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; + int32_t _zAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; + int32_t _rxAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; + int32_t _rxAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; + int32_t _ryAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; + int32_t _ryAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; + int32_t _rzAxisMinimum = JOYSTICK_DEFAULT_AXIS_MINIMUM; + int32_t _rzAxisMaximum = JOYSTICK_DEFAULT_AXIS_MAXIMUM; + int32_t _rudderMinimum = JOYSTICK_DEFAULT_SIMULATOR_MINIMUM; + int32_t _rudderMaximum = JOYSTICK_DEFAULT_SIMULATOR_MAXIMUM; + int32_t _throttleMinimum = JOYSTICK_DEFAULT_SIMULATOR_MINIMUM; + int32_t _throttleMaximum = JOYSTICK_DEFAULT_SIMULATOR_MAXIMUM; + int32_t _acceleratorMinimum = JOYSTICK_DEFAULT_SIMULATOR_MINIMUM; + int32_t _acceleratorMaximum = JOYSTICK_DEFAULT_SIMULATOR_MAXIMUM; + int32_t _brakeMinimum = JOYSTICK_DEFAULT_SIMULATOR_MINIMUM; + int32_t _brakeMaximum = JOYSTICK_DEFAULT_SIMULATOR_MAXIMUM; + int32_t _steeringMinimum = JOYSTICK_DEFAULT_SIMULATOR_MINIMUM; + int32_t _steeringMaximum = JOYSTICK_DEFAULT_SIMULATOR_MAXIMUM; + + uint8_t _hidReportId; + uint8_t _hidReportSize; + + USBHID HID; + +protected: + int buildAndSet16BitValue(bool includeValue, int32_t value, int32_t valueMinimum, int32_t valueMaximum, int32_t actualMinimum, int32_t actualMaximum, uint8_t dataLocation[]); + int buildAndSetAxisValue(bool includeAxis, int32_t axisValue, int32_t axisMinimum, int32_t axisMaximum, uint8_t dataLocation[]); + int buildAndSetSimulationValue(bool includeValue, int32_t value, int32_t valueMinimum, int32_t valueMaximum, uint8_t dataLocation[]); + +public: + uint8_t *customHidReportDescriptor; + int hidReportDescriptorSize; + + Joystick_( + uint8_t hidReportId = JOYSTICK_DEFAULT_REPORT_ID, + uint8_t joystickType = JOYSTICK_TYPE_JOYSTICK, + uint8_t buttonCount = JOYSTICK_DEFAULT_BUTTON_COUNT, + uint8_t hatSwitchCount = JOYSTICK_DEFAULT_HATSWITCH_COUNT, + bool includeXAxis = true, + bool includeYAxis = true, + bool includeZAxis = true, + bool includeRxAxis = true, + bool includeRyAxis = true, + bool includeRzAxis = true, + bool includeRudder = true, + bool includeThrottle = true, + bool includeAccelerator = true, + bool includeBrake = true, + bool includeSteering = true); + + void begin(bool initAutoSendState = true, uint8_t interval_ms = 2); + + void end(); + + // Set Range Functions + inline void setXAxisRange(int32_t minimum, int32_t maximum) + { + _xAxisMinimum = minimum; + _xAxisMaximum = maximum; + } + inline void setYAxisRange(int32_t minimum, int32_t maximum) + { + _yAxisMinimum = minimum; + _yAxisMaximum = maximum; + } + inline void setZAxisRange(int32_t minimum, int32_t maximum) + { + _zAxisMinimum = minimum; + _zAxisMaximum = maximum; + } + inline void setRxAxisRange(int32_t minimum, int32_t maximum) + { + _rxAxisMinimum = minimum; + _rxAxisMaximum = maximum; + } + inline void setRyAxisRange(int32_t minimum, int32_t maximum) + { + _ryAxisMinimum = minimum; + _ryAxisMaximum = maximum; + } + inline void setRzAxisRange(int32_t minimum, int32_t maximum) + { + _rzAxisMinimum = minimum; + _rzAxisMaximum = maximum; + } + inline void setRudderRange(int32_t minimum, int32_t maximum) + { + _rudderMinimum = minimum; + _rudderMaximum = maximum; + } + inline void setThrottleRange(int32_t minimum, int32_t maximum) + { + _throttleMinimum = minimum; + _throttleMaximum = maximum; + } + inline void setAcceleratorRange(int32_t minimum, int32_t maximum) + { + _acceleratorMinimum = minimum; + _acceleratorMaximum = maximum; + } + inline void setBrakeRange(int32_t minimum, int32_t maximum) + { + _brakeMinimum = minimum; + _brakeMaximum = maximum; + } + inline void setSteeringRange(int32_t minimum, int32_t maximum) + { + _steeringMinimum = minimum; + _steeringMaximum = maximum; + } + + // Set Axis Values + void setXAxis(int32_t value); + void setYAxis(int32_t value); + void setZAxis(int32_t value); + void setRxAxis(int32_t value); + void setRyAxis(int32_t value); + void setRzAxis(int32_t value); + + // Set Simulation Values + void setRudder(int32_t value); + void setThrottle(int32_t value); + void setAccelerator(int32_t value); + void setBrake(int32_t value); + void setSteering(int32_t value); + + void setButton(uint8_t button, uint8_t value); + void pressButton(uint8_t button); + void releaseButton(uint8_t button); + + void setHatSwitch(int8_t hatSwitch, int16_t value); + + void sendState(); + uint16_t _onGetDescriptor(uint8_t* buffer); +}; + + +#endif // JOYSTICK_h \ No newline at end of file diff --git a/ESP32-S3-Analog/cache.h b/ESP32-S3-Analog/cache.h new file mode 100644 index 0000000..5d73956 --- /dev/null +++ b/ESP32-S3-Analog/cache.h @@ -0,0 +1,31 @@ +/*************************************************************** + * * + * Taiko Sanro - Arduino * + * Cache data structure * + * * + * Chris * + * wisaly@gmail.com * + * * + ***************************************************************/ + +#ifndef CACHE_H +#define CACHE_H + +template +class Cache { +public: + Cache() { memset(data_, 0, sizeof(data_)); } + inline void put(T value) { + current_ = (current_ + 1) & (L - 1); + data_[current_] = value; + } + inline T get(int offset = 0) const { + return data_[(current_ + offset) & (L - 1)]; + } + +private: + T data_[L]; + int current_ = 0; +}; + +#endif // CACHE_H \ No newline at end of file diff --git a/ESP32-S3-Analog/extra/bnusio.dll b/ESP32-S3-Analog/extra/bnusio.dll new file mode 100644 index 0000000..daa984d Binary files /dev/null and b/ESP32-S3-Analog/extra/bnusio.dll differ diff --git a/README.md b/README.md index 81add3b..f3330fb 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,40 @@ This project aims to help you develop your own hardware taiko at home. Using a bridge rectifier, all negative values are converted to positive. In other words, it's like the `abs()` function, ensuring that we don't lose any negative voltages. ![Why using bridge rectifiers](./images/bridge_signal.png) + +# Taiko Controller - Analog Input Mode (Beta) + +With ESP32-S2 or ESP32-S3 controllers, instead of keyboard emulation, the drum controller can work as a gamepad and send its axes values to the game (which also must support analog input). In this way, the game can recognize different force levels of the hit. + +If you prefer to use the Arduino Micro/Leonardo board, please refer to the [Arduino XInput Library](https://github.com/dmadison/ArduinoXInput) to implement the gamepad. + +## What You Need + +1. Make your drum or use Taiko Force Lv.5. + +2. Flash `ESP32-S3-Analog/ESP32-S3-Analog.ino` to your controller. + +3. A working ***game***, with these modifications: + + - Backup and replace the `bnusio.dll` file in the game folder with the one here in the `extra/` folder. + + This file is compiled from [this fork](https://github.com/ShikyC/TaikoArcadeLoader/tree/Refactor) and you can compile it by yourself if you want. + + *This modified library only works with a specific game version. If it breaks your game, please clone the original repository, make the corrensponding modifications, and compile it.* + + - Open the `gamecontrollerdb.txt` file in the game folder and add one entry under `#Windows`: + + `030052a8694800006948000000000000,Taiko Controller,+leftx:+a0,+lefty:+a1,+rightx:+a3,+righty:+a4,platform:Windows,` + + This will tell the game that our ESP32 controller is a gamepad called "Taiko Controller", and map the axis to the standard SDL2 library so that the game can recognize the analog inputs. + + - Open the `config.toml` file and add the following lines at the end: + + ``` + [controller] + analog = true + ``` + + Note that with `analog = true`, all the keyboard drum inputs are disabled. Sorry for this but it need further refactoring to make them work together. If you want to switch back to keyboard inputs, set `analog = false`. + +4. Launch the game and enjoy! \ No newline at end of file diff --git a/README_ja-JP.md b/README_ja-JP.md index 1505d7a..9375681 100644 --- a/README_ja-JP.md +++ b/README_ja-JP.md @@ -90,4 +90,41 @@ ブリッジ整流器を使用すると、すべての負の値が正の値に変換されます。言い換えれば、それは`abs()`関数のようなもので、負の電圧を失わないことを保証します。 - ![ブリッジ整流器の使用理由](./images/bridge_signal.png) \ No newline at end of file + ![ブリッジ整流器の使用理由](./images/bridge_signal.png) + +# Taiko Controller - アナログ入力モード(Beta) + +ESP32-S2またはESP32-S3コントローラーを使用すると、キーボードエミュレーションの代わりに、ドラムコントローラーがゲームパッドとして機能し、その軸の値をゲームに送信できます(ゲームもアナログ入力をサポートしている必要があります)。この方法では、ゲームはヒットの異なる力のレベルを認識できます。 + +Arduino Micro/Leonardoボードを使用する場合は、[Arduino XInputライブラリ](https://github.com/dmadison/ArduinoXInput)を参照して、ゲームパッドを実装してください。 + +## 必要なもの + +1. 自分のドラムを作るか、Taiko Force Lv.5を使用してください。詳細はメインブランチをチェックしてください。 + +2. `ESP32-S3-Analog/ESP32-S3-Analog.ino`をコントローラーにフラッシュします。 + +3. 以下の変更を加えたゲーム: + + - ゲームフォルダ内の`bnusio.dll`ファイルをバックアップし、`extra/`フォルダーこちらのファイルで置き換えてください。 + + このファイルは[このフォーク](https://github.com/ShikyC/TaikoArcadeLoader/tree/Refactor)からコンパイルされており、必要に応じて自分でコンパイルすることができます。 + + *この変更されたライブラリは特定のゲームバージョンでのみ機能します。ゲームが壊れた場合は、元のリポジトリをクローンして、対応する変更を加え、コンパイルしてください。* + + - ゲームフォルダ内の`gamecontrollerdb.txt`ファイルを開き、`#Windows`の下に次のエントリを追加します: + + `030052a8694800006948000000000000,Taiko Controller,+leftx:+a0,+lefty:+a1,+rightx:+a3,+righty:+a4,platform:Windows,` + + これにより、ゲームはESP32コントローラーが「Taiko Controller」というゲームパッドであることを認識し、標準のSDL2ライブラリに軸をマップするため、ゲームがアナログ入力を認識できるようになります。 + + - `config.toml`ファイルを開き、最後に次の行を追加します: + + ``` + [controller] + analog = true + ``` + + `analog = true`で、すべてのキーボードドラム入力が無効になります。これはさらなるリファクタリングが必要ですが、一緒に機能させるために申し訳ありません。キーボード入力に戻したい場合は、`analog = false`に設定してください。 + +4. ゲームを起動して楽しんでください! \ No newline at end of file diff --git a/README_zh-CN.md b/README_zh-CN.md index 3ae3edf..4bd1007 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -90,4 +90,41 @@ 使用桥式整流器,所有负值都转换为正值。换句话说,就像`abs()`函数,确保我们不会丢失任何负电压。 - ![为什么使用桥式整流器](./images/bridge_signal.png) \ No newline at end of file + ![为什么使用桥式整流器](./images/bridge_signal.png) + +# Taiko Controller - 模拟输入模式 + +使用ESP32-S2或ESP32-S3控制器,代替键盘仿真,鼓控制器可以作为游戏手柄工作,并将其轴值发送给游戏(游戏也必须支持模拟输入)。这样,游戏可以识别击打的不同力度级别。 + +如果您更喜欢使用Arduino Micro/Leonardo板,请参考[Arduino XInput库](https://github.com/dmadison/ArduinoXInput)来实现游戏手柄。 + +## 你需要什么 + +1. 制作你的鼓或使用Taiko Force Lv.5。详情请查看主分支。 + +2. 将`ESP32-S3-Analog/ESP32-S3-Analog.ino`刷写到你的控制器。 + +3. 一个能正常运行的***游戏***,并进行以下修改: + + - 备份并替换游戏文件夹中的`bnusio.dll`文件,使用这里`extra/`文件夹中的文件。 + + 这个文件是从[这个仓库](https://github.com/ShikyC/TaikoArcadeLoader/tree/Refactor)编译的,如果你愿意,你也可以自己编译。 + + *这个修改过的库只适用于特定版本的游戏。如果它破坏了你的游戏,请克隆[原始仓库](https://github.com/BroGamer4256/TaikoArcadeLoader),进行相应的修改,并编译它。* + + - 打开游戏文件夹中的`gamecontrollerdb.txt`文件,并在`#Windows`下添加一条条目: + + `030052a8694800006948000000000000,Taiko Controller,+leftx:+a0,+lefty:+a1,+rightx:+a3,+righty:+a4,platform:Windows,` + + 这将告诉游戏我们的ESP32控制器是一个名为“Taiko Controller”的游戏手柄,并将轴映射到标准SDL2库,以便游戏能够识别模拟输入。 + + - 打开`config.toml`文件,并在末尾添加以下行: + + ``` + [controller] + analog = true + ``` + + 请注意,使用`analog = true`时,所有键盘鼓输入都将被禁用。对此表示抱歉,但它需要进一步重构才能同时工作。如果你想切换回键盘输入,请设置`analog = false`。 + +4. 开始游玩咚!