Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions headers/addons/analog.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@
#endif

#ifndef SMOOTHING_FACTOR
#define SMOOTHING_FACTOR 5
#define SMOOTHING_FACTOR 2
#endif

#ifndef SMOOTHING_FACTOR2
#define SMOOTHING_FACTOR2 5
#define SMOOTHING_FACTOR2 2
#endif

#ifndef ANALOG_ERROR
Expand All @@ -112,15 +112,21 @@ typedef struct
Pin_t y_pin_adc;
float x_value;
float y_value;
uint16_t x_center;
uint16_t y_center;
uint32_t x_center;
uint32_t y_center;
uint32_t x_min;
uint32_t x_max;
uint32_t y_min;
uint32_t y_max;
float xy_magnitude;
float x_magnitude;
float y_magnitude;
InvertMode analog_invert;
DpadMode analog_dpad;
float x_ema;
float y_ema;
bool x_ema_initialized;
bool y_ema_initialized;
bool ema_option;
float ema_smoothing;
float error_rate;
Expand All @@ -134,6 +140,8 @@ typedef struct

class AnalogInput : public GPAddon {
public:
static bool isAdcPin(Pin_t pin);
static uint16_t readCalibrationSample(Pin_t pin);
virtual bool available();
virtual void setup(); // Analog Setup
virtual void process(); // Analog Process
Expand All @@ -142,9 +150,8 @@ class AnalogInput : public GPAddon {
virtual void reinit() {}
virtual std::string name() { return AnalogName; }
private:
float readPin(int stick_num, Pin_t pin, uint16_t center);
float readPin(Pin_t pin, uint32_t center, uint32_t minimum, uint32_t maximum);
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(int stick_num, adc_instance & adc_inst);
void radialDeadzone(int stick_num, adc_instance & adc_inst);
adc_instance adc_pairs[ADC_COUNT];
Expand Down
8 changes: 8 additions & 0 deletions proto/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,14 @@ message AnalogOptions
optional uint32 joystick_center_y = 25;
optional uint32 joystick_center_x2 = 26;
optional uint32 joystick_center_y2 = 27;
optional uint32 joystick_min_x = 28;
optional uint32 joystick_max_x = 29;
optional uint32 joystick_min_y = 30;
optional uint32 joystick_max_y = 31;
optional uint32 joystick_min_x2 = 32;
optional uint32 joystick_max_x2 = 33;
optional uint32 joystick_min_y2 = 34;
optional uint32 joystick_max_y2 = 35;
}

message TurboOptions
Expand Down
103 changes: 73 additions & 30 deletions src/addons/analog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,40 @@
#include "storagemanager.h"
#include "drivermanager.h"

#include <algorithm>
#include <math.h>

#define ADC_MAX ((1 << 12) - 1) // 4095
#define ADC_PIN_OFFSET 26
#define ANALOG_MAX 1.0f
#define ANALOG_CENTER 0.5f
#define ANALOG_MINIMUM 0.0f
#define ANALOG_MIN_DEADZONE_BAND 0.01f

bool AnalogInput::available() {
return Storage::getInstance().getAddonOptions().analogOptions.enabled;
}

bool AnalogInput::isAdcPin(Pin_t pin) {
return pin >= ADC_PIN_OFFSET && pin < ADC_PIN_OFFSET + 4;
}

uint16_t AnalogInput::readCalibrationSample(Pin_t pin) {
if (!isAdcPin(pin)) {
return 0;
}

adc_gpio_init(pin);
adc_select_input(pin - ADC_PIN_OFFSET);
uint32_t sum = 0;

for (int i = 0; i < 16; i++) {
sum += adc_read();
}

return (sum + 8) / 16;
}

void AnalogInput::setup() {
const AnalogOptions& analogOptions = Storage::getInstance().getAddonOptions().analogOptions;

Expand All @@ -27,49 +49,74 @@ void AnalogInput::setup() {
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].ema_smoothing = analogOptions.smoothing_factor;
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[0].joystick_center_x = analogOptions.joystick_center_x;
adc_pairs[0].joystick_center_y = analogOptions.joystick_center_y;
adc_pairs[0].x_min = analogOptions.joystick_min_x;
adc_pairs[0].x_max = analogOptions.joystick_max_x;
adc_pairs[0].y_min = analogOptions.joystick_min_y;
adc_pairs[0].y_max = analogOptions.joystick_max_y;
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].ema_smoothing = analogOptions.smoothing_factor2;
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;
adc_pairs[1].joystick_center_x = analogOptions.joystick_center_x2;
adc_pairs[1].joystick_center_y = analogOptions.joystick_center_y2;
adc_pairs[1].x_min = analogOptions.joystick_min_x2;
adc_pairs[1].x_max = analogOptions.joystick_max_x2;
adc_pairs[1].y_min = analogOptions.joystick_min_y2;
adc_pairs[1].y_max = analogOptions.joystick_max_y2;


// Setup defaults and helpers
for (int i = 0; i < ADC_COUNT; i++) {
if (!isAdcPin(adc_pairs[i].x_pin)) {
adc_pairs[i].x_pin = -1;
}
if (!isAdcPin(adc_pairs[i].y_pin)) {
adc_pairs[i].y_pin = -1;
}

adc_pairs[i].x_pin_adc = adc_pairs[i].x_pin - ADC_PIN_OFFSET;
adc_pairs[i].y_pin_adc = adc_pairs[i].y_pin - ADC_PIN_OFFSET;
adc_pairs[i].in_deadzone = std::clamp(adc_pairs[i].in_deadzone, ANALOG_MINIMUM, ANALOG_MAX - ANALOG_MIN_DEADZONE_BAND);
adc_pairs[i].out_deadzone = std::clamp(adc_pairs[i].out_deadzone, adc_pairs[i].in_deadzone + ANALOG_MIN_DEADZONE_BAND, ANALOG_MAX);
adc_pairs[i].x_value = ANALOG_CENTER;
adc_pairs[i].y_value = ANALOG_CENTER;
adc_pairs[i].xy_magnitude = 0.0f;
adc_pairs[i].x_magnitude = 0.0f;
adc_pairs[i].y_magnitude = 0.0f;
adc_pairs[i].x_ema = 0.0f;
adc_pairs[i].y_ema = 0.0f;
adc_pairs[i].x_ema_initialized = false;
adc_pairs[i].y_ema_initialized = false;
adc_pairs[i].x_ema = ANALOG_CENTER;
adc_pairs[i].y_ema = ANALOG_CENTER;
const float strength = adc_pairs[i].ema_smoothing;
adc_pairs[i].ema_option &= strength > 0.0f && strength <= 10.0f;
adc_pairs[i].ema_smoothing = adc_pairs[i].ema_option ? powf(10.0f, -0.5f * strength) : 0.0f;

if (adc_pairs[i].error_rate <= 0.0f || adc_pairs[i].error_rate > 1.0f) {
adc_pairs[i].error_rate = 1.0f;
}
}

// Intialize and auto center X/Y for each pair
for (int i = 0; i < ADC_COUNT; i++) {
if(isValidPin(adc_pairs[i].x_pin)) {
adc_gpio_init(adc_pairs[i].x_pin);
if (adc_pairs[i].auto_calibration) {
adc_select_input(adc_pairs[i].x_pin - ADC_PIN_OFFSET);
adc_pairs[i].x_center = adc_read();
adc_pairs[i].x_center = readCalibrationSample(adc_pairs[i].x_pin);
} else {
// if auto calibration is disabled, attempt to use stored manual calibration value
adc_pairs[i].x_center = adc_pairs[i].joystick_center_x;
Expand All @@ -78,8 +125,7 @@ void AnalogInput::setup() {
if(isValidPin(adc_pairs[i].y_pin)) {
adc_gpio_init(adc_pairs[i].y_pin);
if (adc_pairs[i].auto_calibration) {
adc_select_input(adc_pairs[i].y_pin - ADC_PIN_OFFSET);
adc_pairs[i].y_center = adc_read();
adc_pairs[i].y_center = readCalibrationSample(adc_pairs[i].y_pin);
} else {
// if auto calibration is disabled, attempt to use stored manual calibration value
adc_pairs[i].y_center = adc_pairs[i].joystick_center_y;
Expand All @@ -101,31 +147,37 @@ 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(i, adc_pairs[i].x_pin_adc, adc_pairs[i].x_center);
adc_pairs[i].x_value = readPin(adc_pairs[i].x_pin_adc, adc_pairs[i].x_center,
adc_pairs[i].x_min, adc_pairs[i].x_max);
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 (adc_pairs[i].ema_option) {
adc_pairs[i].x_value = emaCalculation(i, adc_pairs[i].x_value, adc_pairs[i].x_ema);
if (adc_pairs[i].x_ema_initialized)
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;
adc_pairs[i].x_ema_initialized = true;
}
}
// Read Y-Axis
if (isValidPin(adc_pairs[i].y_pin)) {
adc_pairs[i].y_value = readPin(i, adc_pairs[i].y_pin_adc, adc_pairs[i].y_center);
adc_pairs[i].y_value = readPin(adc_pairs[i].y_pin_adc, adc_pairs[i].y_center,
adc_pairs[i].y_min, adc_pairs[i].y_max);
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 (adc_pairs[i].ema_option) {
adc_pairs[i].y_value = emaCalculation(i, adc_pairs[i].y_value, adc_pairs[i].y_ema);
if (adc_pairs[i].y_ema_initialized)
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;
adc_pairs[i].y_ema_initialized = true;
}
}
// Look for dead-zones and circularity
adc_pairs[i].xy_magnitude = magnitudeCalculation(i, adc_pairs[i]);
if (adc_pairs[i].xy_magnitude < adc_pairs[i].in_deadzone) {
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 {
Expand All @@ -150,29 +202,20 @@ void AnalogInput::process() {
}
}

float AnalogInput::readPin(int stick_num, Pin_t pin_adc, uint16_t center) {
float AnalogInput::readPin(Pin_t pin_adc, uint32_t center, uint32_t minimum, uint32_t maximum) {
adc_select_input(pin_adc);
uint16_t adc_value = adc_read();
// Apply calibration only if auto calibration is enabled or manual calibration has been performed
// Manual calibration is considered performed if the center value is not 0 (default)
if (adc_pairs[stick_num].auto_calibration || center != 0) {
if (adc_value > center) {
adc_value = map(adc_value, center, ADC_MAX, ADC_MAX / 2, ADC_MAX);
} else if (adc_value == center) {
adc_value = ADC_MAX / 2;
} else {
adc_value = map(adc_value, 0, center, 0, ADC_MAX / 2);
}
// Only a valid calibration changes the raw ADC scaling
if (minimum < center && center < maximum && maximum <= ADC_MAX) {
const float delta = (float)adc_value - center;
const float span = adc_value < center ? center - minimum : maximum - center;
return std::clamp(ANALOG_CENTER + ANALOG_CENTER * (delta / span), ANALOG_MINIMUM, ANALOG_MAX);
}
return ((float)adc_value) / ADC_MAX;
}

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;
return ema_previous + adc_pairs[stick_num].ema_smoothing * (ema_value - ema_previous);
}

float AnalogInput::magnitudeCalculation(int stick_num, adc_instance & adc_inst) {
Expand All @@ -184,7 +227,7 @@ float AnalogInput::magnitudeCalculation(int stick_num, adc_instance & adc_inst)
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);
scaling_factor = std::fmin(scaling_factor, ANALOG_CENTER * adc_inst.error_rate);
}
adc_inst.x_value = ((adc_inst.x_magnitude / adc_inst.xy_magnitude) * scaling_factor) + ANALOG_CENTER;
adc_inst.y_value = ((adc_inst.y_magnitude / adc_inst.xy_magnitude) * scaling_factor) + ANALOG_CENTER;
Expand Down
12 changes: 10 additions & 2 deletions src/config_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,15 +712,23 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, outer_deadzone, DEFAULT_OUTER_DEADZONE);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, auto_calibrate, !!AUTO_CALIBRATE_ENABLED);
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, 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, 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);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, joystick_min_x, 0);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, joystick_max_x, 4095);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, joystick_min_y, 0);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, joystick_max_y, 4095);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, joystick_min_x2, 0);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, joystick_max_x2, 4095);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, joystick_min_y2, 0);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, joystick_max_y2, 4095);

// addonOptions.turboOptions
INIT_UNSET_PROPERTY(config.addonOptions.turboOptions, enabled, !!TURBO_ENABLED);
Expand Down
Loading