Compare commits

..

2 commits

Author SHA1 Message Date
85e15a0955 switch support: it somewhat works ™️ 2025-06-12 21:07:09 +02:00
c3d9294019 switch support: 1st draft (not working for now) 2025-06-11 22:41:29 +02:00
4 changed files with 211 additions and 172 deletions

View file

@ -1,8 +1,5 @@
#include "params.h" #include "params.h"
// Select which player this board is. 1 for P1 and 2 for P2.
#define PLAYER_SELECT 1
const byte inPins[CHANNELS] = { const byte inPins[CHANNELS] = {
P1_L_DON_IN, P1_L_KAT_IN, P1_R_DON_IN, P1_R_KAT_IN P1_L_DON_IN, P1_L_KAT_IN, P1_R_DON_IN, P1_R_KAT_IN
}; };
@ -13,6 +10,16 @@ const byte sensitivityPins[CHANNELS] = {
Cache<int, SAMPLE_CACHE_LENGTH> inputWindow[CHANNELS]; Cache<int, SAMPLE_CACHE_LENGTH> inputWindow[CHANNELS];
unsigned long power[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}; uint axisValues[CHANNELS] = {0, 0, 0, 0};
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4, Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4,
@ -22,9 +29,17 @@ Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4,
void setup() { void setup() {
for (byte i = 0; i < CHANNELS; i++) { for (byte i = 0; i < CHANNELS; i++) {
power[i] = 0; power[i] = 0;
#ifndef RAW_ANALOG_MODE
lastPower[i] = 0;
triggered = false;
#endif
pinMode(inPins[i], INPUT); pinMode(inPins[i], INPUT);
pinMode(sensitivityPins[i], INPUT); pinMode(sensitivityPins[i], INPUT);
} }
#ifndef RAW_ANALOG_MODE
maxIndex = -1;
maxPower = 0;
#endif
USB.PID(0x4869); USB.PID(0x4869);
USB.VID(0x4869); USB.VID(0x4869);
USB.productName("Taiko Controller"); USB.productName("Taiko Controller");
@ -42,20 +57,65 @@ void loop() {
for (byte i = 0; i < CHANNELS; i++) { for (byte i = 0; i < CHANNELS; i++) {
inputWindow[i].put(analogRead(inPins[i])); inputWindow[i].put(analogRead(inPins[i]));
power[i] = power[i] - inputWindow[i].get(1) + inputWindow[i].get(); 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 x = analogRead(sensitivityPins[i]) / 2048.0 - 1;
float x2 = x * x; float x2 = x * x;
float x3 = x2 * x; float x3 = x2 * x;
float x4 = x3 * x; float x4 = x3 * x;
float v = (1.0 + x + 0.5 * x2 + 0.166667 * x3) * power[i]; float v = (1.0 + x + 0.5 * x2 + 0.166667 * x3) * power[i];
axisValues[i] = AXIS_RANGE * (v >= MAX_THRES ? 1 : (v / MAX_THRES)); 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 #if PLAYER_SELECT == 1
Joystick.setXAxis(axisValues[0] > axisValues[1] ? axisValues[0] : -axisValues[1]); Joystick.setXAxis(axisValues[0] > axisValues[1] ? axisValues[0] : -axisValues[1]);
Joystick.setYAxis(axisValues[2] > axisValues[3] ? axisValues[2] : -axisValues[3]); Joystick.setYAxis(axisValues[2] > axisValues[3] ? axisValues[2] : -axisValues[3]);
Joystick.setRxAxis(0);
Joystick.setRyAxis(0);
#elif PLAYER_SELECT == 2 #elif PLAYER_SELECT == 2
Joystick.setXAxis(0);
Joystick.setYAxis(0);
Joystick.setRxAxis(axisValues[0] > axisValues[1] ? axisValues[0] : -axisValues[1]); Joystick.setRxAxis(axisValues[0] > axisValues[1] ? axisValues[0] : -axisValues[1]);
Joystick.setRyAxis(axisValues[2] > axisValues[3] ? axisValues[2] : -axisValues[3]); Joystick.setRyAxis(axisValues[2] > axisValues[3] ? axisValues[2] : -axisValues[3]);
#else
Joystick.setXAxis(0);
Joystick.setYAxis(0);
Joystick.setRxAxis(0);
Joystick.setRyAxis(0);
#endif #endif
Joystick.sendState(); Joystick.sendState();

View file

@ -1,5 +1,34 @@
// Sample window length. Larger values reduce noise but add more latency. // Enable this mode to pass raw analog data to the game without any post-
#define SAMPLE_CACHE_LENGTH 64 // 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 WORK
***********************************************/
// 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) // 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. // To configure the light and heavy thresholds, do it in the game settings.
@ -22,12 +51,22 @@
#define P1_R_DON_SENS_IN 17 #define P1_R_DON_SENS_IN 17
#define P1_R_KAT_SENS_IN 18 #define P1_R_KAT_SENS_IN 18
// Controller axis value range
#define AXIS_RANGE 1023 #define AXIS_RANGE 1023
// Number of input channels for each player #define PLAYERS 2
#define CHANNELS 4 #define CHANNELS 4
// The minimum value to trigger a light hit.
// Disabled if RAW_ANALOG_MODE is on.
#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.
// Disabled if RAW_ANALOG_MODE is on.
#define RESET_TIME 40
#include <USB.h> #include <USB.h>
#include "joystick.h" #include "joystick.h"
#include "cache.h" #include "cache.h"

View file

@ -1,148 +1,48 @@
#include "driver/adc.h"
// #include "driver/i2s.h"
#include "freertos/FreeRTOS.h"
#include "params.h" #include "params.h"
// Sensitivity multipliers for each channel, 1.0 as the baseline.
#define P1_L_DON_SENS 10.0
#define P1_L_KAT_SENS 20.0
#define P1_R_DON_SENS 10.0
#define P1_R_KAT_SENS 20.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
#define PLAYERS 2
#define BUF_LENGH 1024
#define CONV_NUM BUF_LENGH * PLAYERS / CHANNELS
#define TIMES 256
#define GET_UNIT(x) ((x>>3) & 0x1)
const byte inPins[PLAYERS][CHANNELS] = { const byte inPins[PLAYERS][CHANNELS] = {
P1_L_DON_IN, P1_L_KAT_IN, P1_R_DON_IN, P1_R_KAT_IN, 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 P2_L_DON_IN, P2_L_KAT_IN, P2_R_DON_IN, P2_R_KAT_IN
}; };
const float sensitivities[PLAYERS][CHANNELS] = { const float sensitivities[PLAYERS][CHANNELS] = {
P1_L_DON_SENS, P1_L_KAT_SENS, P1_R_DON_SENS, P1_R_KAT_SENS, 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}; P2_L_DON_SENS, P2_L_KAT_SENS, P2_R_DON_SENS, P2_R_KAT_SENS
};
Cache<int, SAMPLE_CACHE_LENGTH> inputWindow[PLAYERS][CHANNELS]; Cache<int, SAMPLE_CACHE_LENGTH> inputWindow[PLAYERS][CHANNELS];
unsigned long power[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}; uint axisValues[PLAYERS][CHANNELS] = {0, 0, 0, 0, 0, 0, 0, 0};
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4, Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_GAMEPAD, 10, 4,
true, true, false, true, true, false, false, false, false, true, true, false, true, true, false,
false, false); false, false, false, false, false);
uint maxVal[PLAYERS] = {0, 0};
uint maxIndex[PLAYERS] = {-1, -1};
adc_channel_t adcChannles[PLAYERS][CHANNELS] = {
ADC_CHANNEL_3,
ADC_CHANNEL_4,
ADC_CHANNEL_5,
ADC_CHANNEL_6,
ADC_CHANNEL_7,
ADC_CHANNEL_0,
ADC_CHANNEL_8,
ADC_CHANNEL_9
};
static void adc_init(adc_channel_t *channel, uint8_t channel_num)
{
adc_digi_init_config_t adc_dma_config = {
.max_store_buf_size = BUF_LENGH,
.conv_num_each_intr = CONV_NUM,
.adc1_chan_mask = BIT3 | BIT4 | BIT5 | BIT6 | BIT7 | BIT0 | BIT8 | BIT9,
.adc2_chan_mask = 0
};
ESP_ERROR_CHECK(adc_digi_initialize(&adc_dma_config));
adc_digi_configuration_t dig_cfg = {
.conv_limit_en = 1,
.conv_limit_num = 4,
.sample_freq_hz = 2 * 1000,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE1,
};
adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
dig_cfg.pattern_num = channel_num;
for (int i = 0; i < channel_num; i++) {
uint8_t unit = GET_UNIT(channel[i]);
uint8_t ch = channel[i] & 0x7;
adc_pattern[i].atten = ADC_ATTEN_DB_11;
adc_pattern[i].channel = ch;
adc_pattern[i].unit = unit;
adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
ESP_LOGI(TAG, "adc_pattern[%d].atten is :%x", i, adc_pattern[i].atten);
ESP_LOGI(TAG, "adc_pattern[%d].channel is :%x", i, adc_pattern[i].channel);
ESP_LOGI(TAG, "adc_pattern[%d].unit is :%x", i, adc_pattern[i].unit);
}
dig_cfg.adc_pattern = adc_pattern;
ESP_ERROR_CHECK(adc_digi_controller_configure(&dig_cfg));
}
void setP1Axes(int index, int value) {
switch (index) {
case 0:
Joystick.setXAxis(value);
Joystick.setYAxis(0);
break;
case 1:
Joystick.setXAxis(-value);
Joystick.setYAxis(0);
break;
case 2:
Joystick.setXAxis(0);
Joystick.setYAxis(value);
break;
case 3:
Joystick.setXAxis(0);
Joystick.setYAxis(-value);
break;
default:
Joystick.setXAxis(0);
Joystick.setYAxis(0);
}
}
void setP2Axes(int index, int value) {
switch (index) {
case 0:
Joystick.setRxAxis(value);
Joystick.setRyAxis(0);
break;
case 1:
Joystick.setRxAxis(-value);
Joystick.setRyAxis(0);
break;
case 2:
Joystick.setRxAxis(0);
Joystick.setRyAxis(value);
break;
case 3:
Joystick.setRxAxis(0);
Joystick.setRyAxis(-value);
break;
default:
Joystick.setXAxis(0);
Joystick.setYAxis(0);
}
}
void setup() { void setup() {
for (byte p = 0; p < PLAYERS; p++) { for (byte p = 0; p < PLAYERS; p++) {
for (byte i = 0; i < CHANNELS; i++) { for (byte i = 0; i < CHANNELS; i++) {
power[p][i] = 0; power[p][i] = 0;
#ifndef RAW_ANALOG_MODE
lastPower[p][i] = 0;
triggered[p] = false;
#endif
pinMode(inPins[p][i], INPUT); pinMode(inPins[p][i], INPUT);
} }
#ifndef RAW_ANALOG_MODE
maxIndex[p] = -1;
maxPower[p] = 0;
#endif
} }
USB.PID(0x4869); USB.PID(0x4869);
USB.VID(0x4869); USB.VID(0x4869);
@ -154,47 +54,57 @@ void setup() {
Joystick.setYAxisRange(-AXIS_RANGE, AXIS_RANGE); Joystick.setYAxisRange(-AXIS_RANGE, AXIS_RANGE);
Joystick.setRxAxisRange(-AXIS_RANGE, AXIS_RANGE); Joystick.setRxAxisRange(-AXIS_RANGE, AXIS_RANGE);
Joystick.setRyAxisRange(-AXIS_RANGE, AXIS_RANGE); Joystick.setRyAxisRange(-AXIS_RANGE, AXIS_RANGE);
} }
void loop() { void loop() {
for (byte p = 0; p < PLAYERS; p++) { for (byte p = 0; p < PLAYERS; p++) {
for (byte i = 0; i < CHANNELS; i++) { for (byte i = 0; i < CHANNELS; i++) {
inputWindow[p][i].put(analogRead(inPins[p][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]) {
for (byte p = 0; p < PLAYERS; p++) { maxPower[p] = lastPower[p][i];
maxVal[p] = 0;
maxIndex[p] = -1;
for (byte i = 0; i < CHANNELS; i++) {
power[p][i] = power[p][i] - inputWindow[p][i].get(1) +
inputWindow[p][i].get();
float v = power[p][i] * sensitivities[p][i];
axisValues[p][i] =
AXIS_RANGE * (v >= MAX_THRES ? 1 : (v / MAX_THRES));
if (axisValues[p][i] > maxVal[p]) {
maxVal[p] = axisValues[p][i];
maxIndex[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 (maxIndex[p] >= 0) { if (triggered[p] && resetTimer[p] >= RESET_TIME) {
if (p == 0) { triggered[p] = false;
setP1Axes(maxIndex[p], maxVal[p]); resetTimer[p] = 0;
} else if (p == 1) { maxPower[p] = 0;
setP2Axes(maxIndex[p], maxVal[p]); maxIndex[p] = -1;
} outputValue[p] = 0;
} else { }
if (p == 0) {
setP1Axes(-1, 0); for (byte i = 0; i < CHANNELS; i++) {
} else if (p == 1) { if (triggered[p] && i == maxIndex[p]) {
setP2Axes(-1, 0); 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(); Joystick.sendState();
} }

View file

@ -6,8 +6,8 @@
// The thresholds are also dependent on SAMPLE_CACHE_LENGTH, if you // The thresholds are also dependent on SAMPLE_CACHE_LENGTH, if you
// changed SAMPLE_CACHE_LENGTH, you should also adjust thresholds // changed SAMPLE_CACHE_LENGTH, you should also adjust thresholds
#define HIT_THRES 1750 #define HIT_THRES 400
#define RESET_THRES 200 #define RESET_THRES 80
// Sampling period in μs, e.g., 500μs = 0.5ms = 2000Hz // Sampling period in μs, e.g., 500μs = 0.5ms = 2000Hz
#define SAMPLING_PERIOD 500 #define SAMPLING_PERIOD 500
@ -36,10 +36,22 @@
#define R_DON_KEY 'j' #define R_DON_KEY 'j'
#define R_KAT_KEY 'k' #define R_KAT_KEY 'k'
// Switch controller output for each channel
// this default config should work for all the 3 game settings
#define L_DON_KEY_NS NSGAMEPAD_DPAD_DOWN
#define L_KAT_KEY_NS NSButton_LeftTrigger
#define R_DON_KEY_NS NSButton_B
#define R_KAT_KEY_NS NSButton_RightTrigger
// Enable debug mode to view analog input values from the Serial // Enable debug mode to view analog input values from the Serial
// Enabling this also disables the keyboard simulation // Enabling this also disables the keyboard simulation
#define DEBUG 0 #define DEBUG 0
// 0 = keyboard; 1 = switch controller
#define controller_mode 1
#include "switch_ESP32.h"
NSGamepad Gamepad;
#include "USB.h" #include "USB.h"
#include "USBHIDKeyboard.h" #include "USBHIDKeyboard.h"
#include "cache.h" #include "cache.h"
@ -56,6 +68,7 @@ unsigned long triggeredTime[CHANNELS];
const byte inPins[] = {L_DON_IN, L_KAT_IN, R_DON_IN, R_KAT_IN}; 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 byte outPins[] = {L_DON_LED, L_KAT_LED, R_DON_LED, R_KAT_LED};
const char outKeys[] = {L_DON_KEY, L_KAT_KEY, R_DON_KEY, R_KAT_KEY}; const char outKeys[] = {L_DON_KEY, L_KAT_KEY, R_DON_KEY, R_KAT_KEY};
const uint8_t outKeysNS[] = {L_DON_KEY_NS, L_KAT_KEY_NS, R_DON_KEY_NS, R_KAT_KEY_NS};
float sensitivities[] = {L_DON_SENS, L_KAT_SENS, R_DON_SENS, R_KAT_SENS}; float sensitivities[] = {L_DON_SENS, L_KAT_SENS, R_DON_SENS, R_KAT_SENS};
short maxIndex; short maxIndex;
@ -75,16 +88,23 @@ void setup() {
maxIndex = -1; maxIndex = -1;
maxPower = 0; maxPower = 0;
lastTime = micros(); lastTime = micros();
#if !DEBUG #if !DEBUG
Keyboard.begin(); if (controller_mode) {
USB.begin(); Gamepad.begin();
#endif } else {
Keyboard.begin();
}
USB.begin();
#endif
} }
void loop() { void loop() {
if (maxIndex != -1 && lastPower[maxIndex] < RESET_THRES) { if (maxIndex != -1 && lastPower[maxIndex] < RESET_THRES) {
triggered = false; triggered = false;
digitalWrite(outPins[maxIndex], LOW); digitalWrite(outPins[maxIndex], LOW);
if (controller_mode) {
Gamepad.releaseAll();
}
maxIndex = -1; maxIndex = -1;
maxPower = 0; maxPower = 0;
} }
@ -99,22 +119,32 @@ void loop() {
maxIndex = i; maxIndex = i;
} }
lastPower[i] = power[i]; lastPower[i] = power[i];
#if DEBUG #if DEBUG
Serial.print(power[i]); Serial.print(power[i]);
Serial.print(" "); Serial.print(" ");
#endif #endif
} }
if (!triggered && maxPower >= HIT_THRES) { if (!triggered && maxPower >= HIT_THRES) {
triggered = true; triggered = true;
digitalWrite(outPins[maxIndex], HIGH); digitalWrite(outPins[maxIndex], HIGH);
#if !DEBUG #if !DEBUG
Keyboard.write(outKeys[maxIndex]); if (controller_mode) {
#endif // special case for the DPAD
if (maxIndex == 0) {
Gamepad.dPad(outKeysNS[maxIndex]);
} else {
Gamepad.press(outKeysNS[maxIndex]);
}
Gamepad.loop();
} else {
Keyboard.write(outKeys[maxIndex]);
}
#endif
} }
#if DEBUG #if DEBUG
Serial.print("\n"); Serial.print("\n");
#endif #endif
unsigned int frameTime = micros() - lastTime; unsigned int frameTime = micros() - lastTime;
if (frameTime < SAMPLING_PERIOD) { if (frameTime < SAMPLING_PERIOD) {