separate all analog settings per stick (#1401)

* separate all analog settings per stick

* remove old variables

* align toggles

* merge fix
This commit is contained in:
NickGuyver
2025-06-22 18:47:05 -07:00
committed by GitHub
parent 9fceeacb2f
commit 4d133f0db7
7 changed files with 482 additions and 256 deletions

View File

@@ -47,30 +47,58 @@
#define FORCED_CIRCULARITY_ENABLED 0
#endif
#ifndef FORCED_CIRCULARITY2_ENABLED
#define FORCED_CIRCULARITY2_ENABLED 0
#endif
#ifndef DEFAULT_INNER_DEADZONE
#define DEFAULT_INNER_DEADZONE 5
#endif
#ifndef DEFAULT_INNER_DEADZONE2
#define DEFAULT_INNER_DEADZONE2 5
#endif
#ifndef DEFAULT_OUTER_DEADZONE
#define DEFAULT_OUTER_DEADZONE 95
#endif
#ifndef DEFAULT_OUTER_DEADZONE2
#define DEFAULT_OUTER_DEADZONE2 95
#endif
#ifndef AUTO_CALIBRATE_ENABLED
#define AUTO_CALIBRATE_ENABLED 0
#endif
#ifndef AUTO_CALIBRATE2_ENABLED
#define AUTO_CALIBRATE2_ENABLED 0
#endif
#ifndef ANALOG_SMOOTHING_ENABLED
#define ANALOG_SMOOTHING_ENABLED 0
#endif
#ifndef ANALOG_SMOOTHING2_ENABLED
#define ANALOG_SMOOTHING2_ENABLED 0
#endif
#ifndef SMOOTHING_FACTOR
#define SMOOTHING_FACTOR 5
#endif
#ifndef SMOOTHING_FACTOR2
#define SMOOTHING_FACTOR2 5
#endif
#ifndef ANALOG_ERROR
#define ANALOG_ERROR 1000
#endif
#ifndef ANALOG_ERROR2
#define ANALOG_ERROR2 1000
#endif
// Analog Module Name
#define AnalogName "Analog"
@@ -93,6 +121,13 @@ typedef struct
DpadMode analog_dpad;
float x_ema;
float y_ema;
bool ema_option;
float ema_smoothing;
float error_rate;
float in_deadzone;
float out_deadzone;
bool auto_calibration;
bool forced_circularity;
} adc_instance;
class AnalogInput : public GPAddon {
@@ -105,19 +140,12 @@ public:
virtual void reinit() {}
virtual std::string name() { return AnalogName; }
private:
float readPin(Pin_t pin, uint16_t center);
float emaCalculation(float ema_value, float ema_previous);
float readPin(int stick_num, Pin_t pin, uint16_t center);
float emaCalculation(int stick_num, float ema_value, float ema_previous);
uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max);
float magnitudeCalculation(adc_instance & adc_inst);
void radialDeadzone(adc_instance & adc_inst);
float magnitudeCalculation(int stick_num, adc_instance & adc_inst);
void radialDeadzone(int stick_num, adc_instance & adc_inst);
adc_instance adc_pairs[ADC_COUNT];
bool ema_option;
float ema_smoothing;
float error_rate;
float in_deadzone;
float out_deadzone;
bool auto_calibration;
bool forced_circularity;
};
#endif // _Analog_H_

View File

@@ -411,6 +411,13 @@ message AnalogOptions
optional bool analog_smoothing = 14;
optional float smoothing_factor = 15;
optional uint32 analog_error = 16;
optional bool analog_smoothing2 = 17;
optional float smoothing_factor2 = 18;
optional uint32 analog_error2 = 19;
optional uint32 inner_deadzone2 = 20;
optional uint32 outer_deadzone2 = 21;
optional bool auto_calibrate2 = 22;
optional bool forced_circularity2 = 23;
}
message TurboOptions

View File

@@ -26,10 +26,25 @@ void AnalogInput::setup() {
adc_pairs[0].y_pin = analogOptions.analogAdc1PinY;
adc_pairs[0].analog_invert = analogOptions.analogAdc1Invert;
adc_pairs[0].analog_dpad = analogOptions.analogAdc1Mode;
adc_pairs[0].ema_option = analogOptions.analog_smoothing;
adc_pairs[0].ema_smoothing = analogOptions.smoothing_factor / 1000.0f;
adc_pairs[0].error_rate = analogOptions.analog_error / 1000.0f;
adc_pairs[0].in_deadzone = analogOptions.inner_deadzone / 100.0f;
adc_pairs[0].out_deadzone = analogOptions.outer_deadzone / 100.0f;
adc_pairs[0].auto_calibration = analogOptions.auto_calibrate;
adc_pairs[0].forced_circularity = analogOptions.forced_circularity;
adc_pairs[1].x_pin = analogOptions.analogAdc2PinX;
adc_pairs[1].y_pin = analogOptions.analogAdc2PinY;
adc_pairs[1].analog_invert = analogOptions.analogAdc2Invert;
adc_pairs[1].analog_dpad = analogOptions.analogAdc2Mode;
adc_pairs[1].ema_option = analogOptions.analog_smoothing2;
adc_pairs[1].ema_smoothing = analogOptions.smoothing_factor2 / 1000.0f;
adc_pairs[1].error_rate = analogOptions.analog_error2 / 1000.0f;
adc_pairs[1].in_deadzone = analogOptions.inner_deadzone2 / 100.0f;
adc_pairs[1].out_deadzone = analogOptions.outer_deadzone2 / 100.0f;
adc_pairs[1].auto_calibration = analogOptions.auto_calibrate2;
adc_pairs[1].forced_circularity = analogOptions.forced_circularity2;
// Setup defaults and helpers
for (int i = 0; i < ADC_COUNT; i++) {
@@ -48,28 +63,19 @@ void AnalogInput::setup() {
for (int i = 0; i < ADC_COUNT; i++) {
if(isValidPin(adc_pairs[i].x_pin)) {
adc_gpio_init(adc_pairs[i].x_pin);
if (analogOptions.auto_calibrate) {
if (adc_pairs[i].auto_calibration) {
adc_select_input(adc_pairs[i].x_pin - ADC_PIN_OFFSET);
adc_pairs[i].x_center = adc_read();
}
}
if(isValidPin(adc_pairs[i].y_pin)) {
adc_gpio_init(adc_pairs[i].y_pin);
if (analogOptions.auto_calibrate) {
if (adc_pairs[i].auto_calibration) {
adc_select_input(adc_pairs[i].y_pin - ADC_PIN_OFFSET);
adc_pairs[i].y_center = adc_read();
}
}
}
// Read options from Analog Options
ema_option = analogOptions.analog_smoothing;
ema_smoothing = analogOptions.smoothing_factor / 1000.0f;
error_rate = analogOptions.analog_error / 1000.0f;
in_deadzone = analogOptions.inner_deadzone / 100.0f;
out_deadzone = analogOptions.outer_deadzone / 100.0f;
auto_calibration = analogOptions.auto_calibrate;
forced_circularity = analogOptions.forced_circularity;
}
void AnalogInput::process() {
@@ -83,35 +89,35 @@ void AnalogInput::process() {
for(int i = 0; i < ADC_COUNT; i++) {
// Read X-Axis
if (isValidPin(adc_pairs[i].x_pin)) {
adc_pairs[i].x_value = readPin(adc_pairs[i].x_pin_adc, adc_pairs[i].x_center);
adc_pairs[i].x_value = readPin(i, adc_pairs[i].x_pin_adc, adc_pairs[i].x_center);
if (adc_pairs[i].analog_invert == InvertMode::INVERT_X ||
adc_pairs[i].analog_invert == InvertMode::INVERT_XY) {
adc_pairs[i].x_value = ANALOG_MAX - adc_pairs[i].x_value;
}
if (ema_option) {
adc_pairs[i].x_value = emaCalculation(adc_pairs[i].x_value, adc_pairs[i].x_ema);
if (adc_pairs[i].ema_option) {
adc_pairs[i].x_value = emaCalculation(i, adc_pairs[i].x_value, adc_pairs[i].x_ema);
adc_pairs[i].x_ema = adc_pairs[i].x_value;
}
}
// Read Y-Axis
if (isValidPin(adc_pairs[i].y_pin)) {
adc_pairs[i].y_value = readPin(adc_pairs[i].y_pin_adc, adc_pairs[i].y_center);
adc_pairs[i].y_value = readPin(i, adc_pairs[i].y_pin_adc, adc_pairs[i].y_center);
if (adc_pairs[i].analog_invert == InvertMode::INVERT_Y ||
adc_pairs[i].analog_invert == InvertMode::INVERT_XY) {
adc_pairs[i].y_value = ANALOG_MAX - adc_pairs[i].y_value;
}
if (ema_option) {
adc_pairs[i].y_value = emaCalculation(adc_pairs[i].y_value, adc_pairs[i].y_ema);
if (adc_pairs[i].ema_option) {
adc_pairs[i].y_value = emaCalculation(i, adc_pairs[i].y_value, adc_pairs[i].y_ema);
adc_pairs[i].y_ema = adc_pairs[i].y_value;
}
}
// Look for dead-zones and circularity
adc_pairs[i].xy_magnitude = magnitudeCalculation(adc_pairs[i]);
if (adc_pairs[i].xy_magnitude < in_deadzone) {
adc_pairs[i].xy_magnitude = magnitudeCalculation(i, adc_pairs[i]);
if (adc_pairs[i].xy_magnitude < adc_pairs[i].in_deadzone) {
adc_pairs[i].x_value = ANALOG_CENTER;
adc_pairs[i].y_value = ANALOG_CENTER;
} else {
radialDeadzone(adc_pairs[i]);
radialDeadzone(i, adc_pairs[i]);
}
if (adc_pairs[i].analog_dpad == DpadMode::DPAD_MODE_LEFT_ANALOG) {
@@ -134,10 +140,10 @@ void AnalogInput::process() {
}
}
float AnalogInput::readPin(Pin_t pin_adc, uint16_t center) {
float AnalogInput::readPin(int stick_num, Pin_t pin_adc, uint16_t center) {
adc_select_input(pin_adc);
uint16_t adc_value = adc_read();
if (auto_calibration) {
if (adc_pairs[stick_num].auto_calibration) {
if (adc_value > center) {
adc_value = map(adc_value, center, ADC_MAX, ADC_MAX / 2, ADC_MAX);
} else if (adc_value == center) {
@@ -149,23 +155,23 @@ float AnalogInput::readPin(Pin_t pin_adc, uint16_t center) {
return ((float)adc_value) / ADC_MAX;
}
float AnalogInput::emaCalculation(float ema_value, float ema_previous) {
return (ema_smoothing * ema_value) + ((1.0f - ema_smoothing) * ema_previous);
float AnalogInput::emaCalculation(int stick_num, float ema_value, float ema_previous) {
return (adc_pairs[stick_num].ema_smoothing * ema_value) + ((1.0f - adc_pairs[stick_num].ema_smoothing) * ema_previous);
}
uint16_t AnalogInput::map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float AnalogInput::magnitudeCalculation(adc_instance & adc_inst) {
float AnalogInput::magnitudeCalculation(int stick_num, adc_instance & adc_inst) {
adc_inst.x_magnitude = adc_inst.x_value - ANALOG_CENTER;
adc_inst.y_magnitude = adc_inst.y_value - ANALOG_CENTER;
return error_rate * std::sqrt((adc_inst.x_magnitude * adc_inst.x_magnitude) + (adc_inst.y_magnitude * adc_inst.y_magnitude));
return adc_pairs[stick_num].error_rate * std::sqrt((adc_inst.x_magnitude * adc_inst.x_magnitude) + (adc_inst.y_magnitude * adc_inst.y_magnitude));
}
void AnalogInput::radialDeadzone(adc_instance & adc_inst) {
float scaling_factor = (adc_inst.xy_magnitude - in_deadzone) / (out_deadzone - in_deadzone);
if (forced_circularity == true) {
void AnalogInput::radialDeadzone(int stick_num, adc_instance & adc_inst) {
float scaling_factor = (adc_inst.xy_magnitude - adc_pairs[stick_num].in_deadzone) / (adc_pairs[stick_num].out_deadzone - adc_pairs[stick_num].in_deadzone);
if (adc_pairs[stick_num].forced_circularity == true) {
scaling_factor = std::fmin(scaling_factor, ANALOG_CENTER);
}
adc_inst.x_value = ((adc_inst.x_magnitude / adc_inst.xy_magnitude) * scaling_factor) + ANALOG_CENTER;

View File

@@ -609,6 +609,13 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, analog_smoothing, !!ANALOG_SMOOTHING_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, smoothing_factor, !!SMOOTHING_FACTOR);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, analog_error, ANALOG_ERROR);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, analog_smoothing2, !!ANALOG_SMOOTHING2_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, smoothing_factor2, !!SMOOTHING_FACTOR2);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, analog_error2, ANALOG_ERROR2);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, inner_deadzone2, DEFAULT_INNER_DEADZONE2);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, outer_deadzone2, DEFAULT_OUTER_DEADZONE2);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, auto_calibrate2, !!AUTO_CALIBRATE2_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, forced_circularity2, !!FORCED_CIRCULARITY2_ENABLED);
// addonOptions.turboOptions
INIT_UNSET_PROPERTY(config.addonOptions.turboOptions, enabled, !!TURBO_ENABLED);

View File

@@ -1508,12 +1508,19 @@ std::string setAddonOptions()
docToValue(analogOptions.analogAdc2Mode, doc, "analogAdc2Mode");
docToValue(analogOptions.analogAdc2Invert, doc, "analogAdc2Invert");
docToValue(analogOptions.forced_circularity, doc, "forced_circularity");
docToValue(analogOptions.forced_circularity2, doc, "forced_circularity2");
docToValue(analogOptions.inner_deadzone, doc, "inner_deadzone");
docToValue(analogOptions.inner_deadzone2, doc, "inner_deadzone2");
docToValue(analogOptions.outer_deadzone, doc, "outer_deadzone");
docToValue(analogOptions.outer_deadzone2, doc, "outer_deadzone2");
docToValue(analogOptions.auto_calibrate, doc, "auto_calibrate");
docToValue(analogOptions.auto_calibrate2, doc, "auto_calibrate2");
docToValue(analogOptions.analog_smoothing, doc, "analog_smoothing");
docToValue(analogOptions.analog_smoothing2, doc, "analog_smoothing2");
docToValue(analogOptions.smoothing_factor, doc, "smoothing_factor");
docToValue(analogOptions.smoothing_factor2, doc, "smoothing_factor2");
docToValue(analogOptions.analog_error, doc, "analog_error");
docToValue(analogOptions.analog_error2, doc, "analog_error2");
docToValue(analogOptions.enabled, doc, "AnalogInputEnabled");
BootselButtonOptions& bootselButtonOptions = Storage::getInstance().getAddonOptions().bootselButtonOptions;
@@ -1930,12 +1937,19 @@ std::string getAddonOptions()
writeDoc(doc, "analogAdc2Mode", analogOptions.analogAdc2Mode);
writeDoc(doc, "analogAdc2Invert", analogOptions.analogAdc2Invert);
writeDoc(doc, "forced_circularity", analogOptions.forced_circularity);
writeDoc(doc, "forced_circularity2", analogOptions.forced_circularity2);
writeDoc(doc, "inner_deadzone", analogOptions.inner_deadzone);
writeDoc(doc, "inner_deadzone2", analogOptions.inner_deadzone2);
writeDoc(doc, "outer_deadzone", analogOptions.outer_deadzone);
writeDoc(doc, "outer_deadzone2", analogOptions.outer_deadzone2);
writeDoc(doc, "auto_calibrate", analogOptions.auto_calibrate);
writeDoc(doc, "auto_calibrate2", analogOptions.auto_calibrate2);
writeDoc(doc, "analog_smoothing", analogOptions.analog_smoothing);
writeDoc(doc, "analog_smoothing2", analogOptions.analog_smoothing2);
writeDoc(doc, "smoothing_factor", analogOptions.smoothing_factor);
writeDoc(doc, "smoothing_factor2", analogOptions.smoothing_factor2);
writeDoc(doc, "analog_error", analogOptions.analog_error);
writeDoc(doc, "analog_error2", analogOptions.analog_error2);
writeDoc(doc, "AnalogInputEnabled", analogOptions.enabled);
const BootselButtonOptions& bootselButtonOptions = Storage::getInstance().getAddonOptions().bootselButtonOptions;

View File

@@ -460,12 +460,19 @@ app.get('/api/getAddonsOptions', (req, res) => {
analogAdc2Mode: 2,
analogAdc2Invert: 0,
forced_circularity: 0,
forced_circularity2: 0,
inner_deadzone: 5,
inner_deadzone2: 5,
outer_deadzone: 95,
outer_deadzone2: 95,
auto_calibrate: 0,
auto_calibrate2: 0,
analog_smoothing: 0,
analog_smoothing2: 0,
smoothing_factor: 5,
smoothing_factor2: 5,
analog_error: 1000,
analog_error2: 1000,
bootselButtonMap: 0,
buzzerPin: -1,
buzzerEnablePin: -1,

View File

@@ -1,6 +1,6 @@
import { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { FormCheck, Row } from 'react-bootstrap';
import { FormCheck, Row, Tab, Tabs } from 'react-bootstrap';
import * as yup from 'yup';
import Section from '../Components/Section';
@@ -80,30 +80,58 @@ export const analogScheme = {
.number()
.label('Force Circularity')
.validateRangeWhenValue('AnalogInputEnabled', 0, 1),
forced_circularity2: yup
.number()
.label('Force Circularity')
.validateRangeWhenValue('AnalogInputEnabled', 0, 1),
inner_deadzone: yup
.number()
.label('Inner Deadzone Size (%)')
.validateRangeWhenValue('AnalogInputEnabled', 0, 100),
inner_deadzone2: yup
.number()
.label('Inner Deadzone Size (%)')
.validateRangeWhenValue('AnalogInputEnabled', 0, 100),
outer_deadzone: yup
.number()
.label('Outer Deadzone Size (%)')
.validateRangeWhenValue('AnalogInputEnabled', 0, 100),
outer_deadzone2: yup
.number()
.label('Outer Deadzone Size (%)')
.validateRangeWhenValue('AnalogInputEnabled', 0, 100),
auto_calibrate: yup
.number()
.label('Auto Calibration')
.validateRangeWhenValue('AnalogInputEnabled', 0, 1),
auto_calibrate2: yup
.number()
.label('Auto Calibration')
.validateRangeWhenValue('AnalogInputEnabled', 0, 1),
analog_smoothing: yup
.number()
.label('Analog Smoothing')
.validateRangeWhenValue('AnalogInputEnabled', 0, 1),
analog_smoothing2: yup
.number()
.label('Analog Smoothing 2')
.validateRangeWhenValue('AnalogInputEnabled', 0, 1),
smoothing_factor: yup
.number()
.label('Smoothing Factor')
.validateRangeWhenValue('AnalogInputEnabled', 0, 100),
smoothing_factor2: yup
.number()
.label('Smoothing Factor 2')
.validateRangeWhenValue('AnalogInputEnabled', 0, 100),
analog_error: yup
.number()
.label('Error Rate')
.validateSelectionWhenValue('AnalogInputEnabled', ANALOG_ERROR_RATES),
analog_error2: yup
.number()
.label('Error Rate 2')
.validateSelectionWhenValue('AnalogInputEnabled', ANALOG_ERROR_RATES),
};
export const analogState = {
@@ -118,11 +146,17 @@ export const analogState = {
analogAdc2Invert: 0,
forced_circularity: 0,
inner_deadzone: 5,
inner_deadzone2: 5,
outer_deadzone: 95,
outer_deadzone2: 95,
auto_calibrate: 0,
auto_calibrate2: 0,
analog_smoothing: 0,
analog_smoothing2: 0,
smoothing_factor: 5,
smoothing_factor2: 5,
analog_error: 1,
analog_error2: 1,
};
const Analog = ({ values, errors, handleChange, handleCheckbox }) => {
@@ -152,222 +186,345 @@ const Analog = ({ values, errors, handleChange, handleCheckbox }) => {
pins: availableAnalogPins.join(', '),
})}
</div>
<Row className="mb-3">
<FormSelect
label={t('AddonsConfig:analog-adc-1-pin-x-label')}
name="analogAdc1PinX"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc1PinX}
error={errors.analogAdc1PinX}
isInvalid={errors.analogAdc1PinX}
onChange={handleChange}
<Tabs
defaultActiveKey="analog1Config"
id="analogConfigTabs"
className="mb-3 pb-0"
fill
>
<AnalogPinOptions />
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-1-pin-y-label')}
name="analogAdc1PinY"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc1PinY}
error={errors.analogAdc1PinY}
isInvalid={errors.analogAdc1PinY}
onChange={handleChange}
>
<AnalogPinOptions />
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-1-mode-label')}
name="analogAdc1Mode"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc1Mode}
error={errors.analogAdc1Mode}
isInvalid={errors.analogAdc1Mode}
onChange={handleChange}
>
{ANALOG_STICK_MODES.map((o, i) => (
<option key={`button-analogAdc1Mode-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-1-invert-label')}
name="analogAdc1Invert"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc1Invert}
error={errors.analogAdc1Invert}
isInvalid={errors.analogAdc1Invert}
onChange={handleChange}
>
{INVERT_MODES.map((o, i) => (
<option
key={`button-analogAdc1Invert-option-${i}`}
value={o.value}
>
{o.label}
</option>
))}
</FormSelect>
</Row>
<Row className="mb-3">
<FormSelect
label={t('AddonsConfig:analog-adc-2-pin-x-label')}
name="analogAdc2PinX"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc2PinX}
error={errors.analogAdc2PinX}
isInvalid={errors.analogAdc2PinX}
onChange={handleChange}
>
<AnalogPinOptions />
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-2-pin-y-label')}
name="analogAdc2PinY"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc2PinY}
error={errors.analogAdc2PinY}
isInvalid={errors.analogAdc2PinY}
onChange={handleChange}
>
<AnalogPinOptions />
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-2-mode-label')}
name="analogAdc2Mode"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc2Mode}
error={errors.analogAdc2Mode}
isInvalid={errors.analogAdc2Mode}
onChange={handleChange}
>
{ANALOG_STICK_MODES.map((o, i) => (
<option key={`button-analogAdc2Mode-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-2-invert-label')}
name="analogAdc2Invert"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc2Invert}
error={errors.analogAdc2Invert}
isInvalid={errors.analogAdc2Invert}
onChange={handleChange}
>
{INVERT_MODES.map((o, i) => (
<option
key={`button-analogAdc2Invert-option-${i}`}
value={o.value}
>
{o.label}
</option>
))}
</FormSelect>
</Row>
<Row className="mb-3">
<FormControl
type="number"
label={t('AddonsConfig:inner-deadzone-size')}
name="inner_deadzone"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.inner_deadzone}
error={errors.inner_deadzone}
isInvalid={errors.inner_deadzone}
onChange={handleChange}
min={0}
max={100}
/>
<FormControl
type="number"
label={t('AddonsConfig:outer-deadzone-size')}
name="outer_deadzone"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.outer_deadzone}
error={errors.outer_deadzone}
isInvalid={errors.outer_deadzone}
onChange={handleChange}
min={0}
max={100}
/>
<FormSelect
hidden={!values.forced_circularity}
label={t('AddonsConfig:analog-error-label')}
name="analog_error"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.analog_error}
onChange={handleChange}
>
{ANALOG_ERROR_RATES.map((o, i) => (
<option key={`analog_error-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormControl
hidden={!values.analog_smoothing}
type="number"
label={t('AddonsConfig:smoothing-factor')}
name="smoothing_factor"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.smoothing_factor}
error={errors.smoothing_factor}
isInvalid={errors.smoothing_factor}
onChange={handleChange}
min={0}
max={100}
/>
<FormCheck
label={t('AddonsConfig:analog-force-circularity')}
type="switch"
id="Forced_circularity"
className="col-sm-3 ms-2"
isInvalid={false}
checked={Boolean(values.forced_circularity)}
onChange={(e) => {
handleCheckbox('forced_circularity', values);
handleChange(e);
}}
/>
<FormCheck
label={t('AddonsConfig:analog-auto-calibrate')}
type="switch"
id="Auto_calibrate"
className="col-sm-3 ms-2"
isInvalid={false}
checked={Boolean(values.auto_calibrate)}
onChange={(e) => {
handleCheckbox('auto_calibrate', values);
handleChange(e);
}}
/>
<FormCheck
label={t('AddonsConfig:analog-smoothing')}
type="switch"
id="Analog_smoothing"
className="col-sm-3 ms-2"
isInvalid={false}
checked={Boolean(values.analog_smoothing)}
onChange={(e) => {
handleCheckbox('analog_smoothing', values);
handleChange(e);
}}
/>
</Row>
<Tab
key="analog1Config"
eventKey="analog1Config"
title={t('Analog Stick 1')}
>
<Row className="mb-3">
<FormSelect
label={t('AddonsConfig:analog-adc-1-pin-x-label')}
name="analogAdc1PinX"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc1PinX}
error={errors.analogAdc1PinX}
isInvalid={errors.analogAdc1PinX}
onChange={handleChange}
>
<AnalogPinOptions />
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-1-pin-y-label')}
name="analogAdc1PinY"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc1PinY}
error={errors.analogAdc1PinY}
isInvalid={errors.analogAdc1PinY}
onChange={handleChange}
>
<AnalogPinOptions />
</FormSelect>
<Row className="mb-3">
<FormSelect
label={t('AddonsConfig:analog-adc-1-mode-label')}
name="analogAdc1Mode"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc1Mode}
error={errors.analogAdc1Mode}
isInvalid={errors.analogAdc1Mode}
onChange={handleChange}
>
{ANALOG_STICK_MODES.map((o, i) => (
<option key={`button-analogAdc1Mode-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-1-invert-label')}
name="analogAdc1Invert"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc1Invert}
error={errors.analogAdc1Invert}
isInvalid={errors.analogAdc1Invert}
onChange={handleChange}
>
{INVERT_MODES.map((o, i) => (
<option
key={`button-analogAdc1Invert-option-${i}`}
value={o.value}
>
{o.label}
</option>
))}
</FormSelect>
</Row>
<Row className="mb-3">
<FormControl
type="number"
label={t('AddonsConfig:inner-deadzone-size')}
name="inner_deadzone"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.inner_deadzone}
error={errors.inner_deadzone}
isInvalid={errors.inner_deadzone}
onChange={handleChange}
min={0}
max={100}
/>
<FormControl
type="number"
label={t('AddonsConfig:outer-deadzone-size')}
name="outer_deadzone"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.outer_deadzone}
error={errors.outer_deadzone}
isInvalid={errors.outer_deadzone}
onChange={handleChange}
min={0}
max={100}
/>
</Row>
<Row className="mb-3">
<FormCheck
label={t('AddonsConfig:analog-smoothing')}
type="switch"
id="Analog_smoothing"
className="col-sm-3 ms-3"
isInvalid={false}
checked={Boolean(values.analog_smoothing)}
onChange={(e) => {
handleCheckbox('analog_smoothing', values);
handleChange(e);
}}
/>
<FormControl
hidden={!values.analog_smoothing}
type="number"
label={t('AddonsConfig:smoothing-factor')}
name="smoothing_factor"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.smoothing_factor}
error={errors.smoothing_factor}
isInvalid={errors.smoothing_factor}
onChange={handleChange}
min={0}
max={100}
/>
</Row>
<Row className="mb-3">
<FormCheck
label={t('AddonsConfig:analog-force-circularity')}
type="switch"
id="Forced_circularity"
className="col-sm-3 ms-3"
isInvalid={false}
checked={Boolean(values.forced_circularity)}
onChange={(e) => {
handleCheckbox('forced_circularity', values);
handleChange(e);
}}
/>
<FormSelect
hidden={!values.forced_circularity}
label={t('AddonsConfig:analog-error-label')}
name="analog_error"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.analog_error}
onChange={handleChange}
>
{ANALOG_ERROR_RATES.map((o, i) => (
<option key={`analog_error-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
</Row>
<FormCheck
label={t('AddonsConfig:analog-auto-calibrate')}
type="switch"
id="Auto_calibrate"
className="col-sm-3 ms-3"
isInvalid={false}
checked={Boolean(values.auto_calibrate)}
onChange={(e) => {
handleCheckbox('auto_calibrate', values);
handleChange(e);
}}
/>
</Row>
</Tab>
<Tab
key="analog2Config"
eventKey="analog2Config"
title={t('Analog Stick 2')}
>
<Row className="mb-3">
<FormSelect
label={t('AddonsConfig:analog-adc-2-pin-x-label')}
name="analogAdc2PinX"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc2PinX}
error={errors.analogAdc2PinX}
isInvalid={errors.analogAdc2PinX}
onChange={handleChange}
>
<AnalogPinOptions />
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-2-pin-y-label')}
name="analogAdc2PinY"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc2PinY}
error={errors.analogAdc2PinY}
isInvalid={errors.analogAdc2PinY}
onChange={handleChange}
>
<AnalogPinOptions />
</FormSelect>
<Row className="mb-3">
<FormSelect
label={t('AddonsConfig:analog-adc-2-mode-label')}
name="analogAdc2Mode"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc2Mode}
error={errors.analogAdc2Mode}
isInvalid={errors.analogAdc2Mode}
onChange={handleChange}
>
{ANALOG_STICK_MODES.map((o, i) => (
<option key={`button-analogAdc2Mode-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
<FormSelect
label={t('AddonsConfig:analog-adc-2-invert-label')}
name="analogAdc2Invert"
className="form-select-sm"
groupClassName="col-sm-3 mb-3"
value={values.analogAdc2Invert}
error={errors.analogAdc2Invert}
isInvalid={errors.analogAdc2Invert}
onChange={handleChange}
>
{INVERT_MODES.map((o, i) => (
<option
key={`button-analogAdc2Invert-option-${i}`}
value={o.value}
>
{o.label}
</option>
))}
</FormSelect>
</Row>
<Row className="mb-3">
<FormControl
type="number"
label={t('AddonsConfig:inner-deadzone-size')}
name="inner_deadzone2"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.inner_deadzone2}
error={errors.inner_deadzone2}
isInvalid={errors.inner_deadzone2}
onChange={handleChange}
min={0}
max={100}
/>
<FormControl
type="number"
label={t('AddonsConfig:outer-deadzone-size')}
name="outer_deadzone2"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.outer_deadzone2}
error={errors.outer_deadzone2}
isInvalid={errors.outer_deadzone2}
onChange={handleChange}
min={0}
max={100}
/>
</Row>
<Row className="mb-3">
<FormCheck
label={t('AddonsConfig:analog-smoothing')}
type="switch"
id="Analog_smoothing2"
className="col-sm-3 ms-3"
isInvalid={false}
checked={Boolean(values.analog_smoothing2)}
onChange={(e) => {
handleCheckbox('analog_smoothing2', values);
handleChange(e);
}}
/>
<FormControl
hidden={!values.analog_smoothing2}
type="number"
label={t('AddonsConfig:smoothing-factor')}
name="smoothing_factor2"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.smoothing_factor2}
error={errors.smoothing_factor2}
isInvalid={errors.smoothing_factor2}
onChange={handleChange}
min={0}
max={100}
/>
</Row>
<Row className="mb-3">
<FormCheck
label={t('AddonsConfig:analog-force-circularity')}
type="switch"
id="Forced_circularity2"
className="col-sm-3 ms-3"
isInvalid={false}
checked={Boolean(values.forced_circularity2)}
onChange={(e) => {
handleCheckbox('forced_circularity2', values);
handleChange(e);
}}
/>
<FormSelect
hidden={!values.forced_circularity2}
label={t('AddonsConfig:analog-error-label')}
name="analog_error2"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.analog_error2}
onChange={handleChange}
>
{ANALOG_ERROR_RATES.map((o, i) => (
<option key={`analog_error-option-${i}`} value={o.value}>
{o.label}
</option>
))}
</FormSelect>
</Row>
<FormCheck
label={t('AddonsConfig:analog-auto-calibrate')}
type="switch"
id="Auto_calibrate2"
className="col-sm-3 ms-3"
isInvalid={false}
checked={Boolean(values.auto_calibrate2)}
onChange={(e) => {
handleCheckbox('auto_calibrate2', values);
handleChange(e);
}}
/>
</Row>
</Tab>
</Tabs>
</div>
<FormCheck
label={t('Common:switch-enabled')}