v1.0.0-alpha3
This commit is contained in:
250
Firmware/ESP32/main/Range.h
Normal file
250
Firmware/ESP32/main/Range.h
Normal file
@@ -0,0 +1,250 @@
|
||||
#ifndef _RANGE_H_
|
||||
#define _RANGE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include "Board/ogxm_log.h"
|
||||
|
||||
namespace Range {
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MAX = std::numeric_limits<T>::max();
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MIN = std::numeric_limits<T>::min();
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T MID =
|
||||
[] {
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return MAX<T> / 2 + 1;
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
static_assert(MID<uint8_t> == 128, "MID<uint8_t> != 128");
|
||||
static_assert(MID<int8_t> == 0, "MID<int8_t> != 0");
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr uint16_t NUM_BITS = sizeof(T) * 8;
|
||||
|
||||
//Maximum value for a given number of bits, result will be signed/unsigned depending on type
|
||||
template<typename T, uint8_t bits>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T BITS_MAX()
|
||||
{
|
||||
static_assert(bits <= NUM_BITS<T>, "BITS_MAX: Return type cannot represet maximum bit value");
|
||||
static_assert(bits <= 64, "BITS_MAX: Bits exceed 64");
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return (1ULL << bits) - 1;
|
||||
}
|
||||
return (1LL << (bits - 1)) - 1;
|
||||
}
|
||||
static_assert(BITS_MAX<int16_t, 10>() == 511, "BITS_MAX<int16_t>(10) != 511");
|
||||
static_assert(BITS_MAX<uint16_t, 10>() == 1023, "BITS_MAX<uint16_t>(10) != 1023");
|
||||
|
||||
//Minimum value for a given number of bits, result will be signed/unsigned depending on type
|
||||
template<typename T, uint8_t bits>
|
||||
requires std::is_integral_v<T>
|
||||
constexpr T BITS_MIN()
|
||||
{
|
||||
static_assert(bits <= NUM_BITS<T>, "BITS_MIN: Return type cannot represet minimum bit value");
|
||||
static_assert(bits <= 64, "BITS_MIN: Bits exceed 64");
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return static_cast<T>(-(1LL << (bits - 1)));
|
||||
}
|
||||
static_assert(BITS_MIN<int16_t, 10>() == -512, "BITS_MIN<int16_t>(10) != -512");
|
||||
static_assert(BITS_MIN<uint16_t, 10>() == 0, "BITS_MIN<uint16_t>(10) != 0");
|
||||
|
||||
template <typename T>
|
||||
static inline T invert(T value)
|
||||
{
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
return Range::MAX<T> - value;
|
||||
}
|
||||
return (value == Range::MIN<T>) ? Range::MAX<T> : -value;
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
requires std::is_integral_v<To> && std::is_integral_v<From>
|
||||
static inline To clamp(From value)
|
||||
{
|
||||
if constexpr (std::is_signed_v<From> != std::is_signed_v<To>)
|
||||
{
|
||||
using CommonType = std::common_type_t<To, From>;
|
||||
return static_cast<To>((static_cast<CommonType>(value) < static_cast<CommonType>(Range::MIN<To>))
|
||||
? Range::MIN<To>
|
||||
: (static_cast<CommonType>(value) > static_cast<CommonType>(Range::MAX<To>))
|
||||
? Range::MAX<To>
|
||||
: static_cast<CommonType>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<To>((value < Range::MIN<To>)
|
||||
? Range::MIN<To>
|
||||
: (value > Range::MAX<To>)
|
||||
? Range::MAX<To>
|
||||
: value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
static inline T clamp(T value, T min, T max)
|
||||
{
|
||||
return (value < min) ? min : (value > max) ? max : value;
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
requires std::is_integral_v<To> && std::is_integral_v<From>
|
||||
static inline To scale(From value, From min_from, From max_from, To min_to, To max_to)
|
||||
{
|
||||
if constexpr (std::is_unsigned_v<From> && std::is_unsigned_v<To>)
|
||||
{
|
||||
// Both unsigned
|
||||
uint64_t scaled = static_cast<uint64_t>(value - min_from) *
|
||||
(max_to - min_to) /
|
||||
(max_from - min_from) + min_to;
|
||||
return static_cast<To>(scaled);
|
||||
}
|
||||
else if constexpr (std::is_signed_v<From> && std::is_unsigned_v<To>)
|
||||
{
|
||||
// From signed, To unsigned
|
||||
uint64_t shift_from = static_cast<uint64_t>(-min_from);
|
||||
uint64_t u_value = static_cast<uint64_t>(value) + shift_from;
|
||||
uint64_t u_min_from = static_cast<uint64_t>(min_from) + shift_from;
|
||||
uint64_t u_max_from = static_cast<uint64_t>(max_from) + shift_from;
|
||||
|
||||
uint64_t scaled = (u_value - u_min_from) *
|
||||
(max_to - min_to) /
|
||||
(u_max_from - u_min_from) + min_to;
|
||||
return static_cast<To>(scaled);
|
||||
}
|
||||
else if constexpr (std::is_unsigned_v<From> && std::is_signed_v<To>)
|
||||
{
|
||||
// From unsigned, To signed
|
||||
uint64_t shift_to = static_cast<uint64_t>(-min_to);
|
||||
uint64_t scaled = static_cast<uint64_t>(value - min_from) *
|
||||
(static_cast<uint64_t>(max_to) + shift_to - static_cast<uint64_t>(min_to) - shift_to) /
|
||||
(max_from - min_from) + static_cast<uint64_t>(min_to) + shift_to;
|
||||
return static_cast<To>(scaled - shift_to);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Both signed
|
||||
int64_t shift_from = -min_from;
|
||||
int64_t shift_to = -min_to;
|
||||
|
||||
int64_t scaled = (static_cast<int64_t>(value) + shift_from - (min_from + shift_from)) *
|
||||
(max_to + shift_to - (min_to + shift_to)) /
|
||||
(max_from - min_from) + (min_to + shift_to);
|
||||
return static_cast<To>(scaled - shift_to);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
static inline To scale(From value)
|
||||
{
|
||||
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, Range::MIN<To>, Range::MAX<To>);
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
static inline To scale(From value, To min_to, To max_to)
|
||||
{
|
||||
return scale<To>(value, Range::MIN<From>, Range::MAX<From>, min_to, max_to);
|
||||
}
|
||||
|
||||
//Cast value to signed/unsigned for accurate scaling
|
||||
template <typename To, uint8_t bits, typename From>
|
||||
static inline To scale_from_bits(From value)
|
||||
{
|
||||
return scale<To>(
|
||||
value,
|
||||
BITS_MIN<From, bits>(),
|
||||
BITS_MAX<From, bits>(),
|
||||
Range::MIN<To>,
|
||||
Range::MAX<To>);
|
||||
}
|
||||
|
||||
//Cast value to signed/unsigned for accurate scaling
|
||||
template <typename To, uint8_t bits, typename From>
|
||||
static inline To scale_to_bits(From value)
|
||||
{
|
||||
return scale<To>(
|
||||
value,
|
||||
Range::MIN<From>,
|
||||
Range::MAX<From>,
|
||||
BITS_MIN<To, bits>(),
|
||||
BITS_MAX<To, bits>());
|
||||
}
|
||||
|
||||
} // namespace Range
|
||||
|
||||
namespace Scale //Scale and invert values
|
||||
{
|
||||
static inline uint8_t int16_to_uint8(int16_t value)
|
||||
{
|
||||
uint16_t shifted_value = static_cast<uint16_t>(value + Range::MID<uint16_t>);
|
||||
return static_cast<uint8_t>(shifted_value >> 8);
|
||||
}
|
||||
static inline uint16_t int16_to_uint16(int16_t value)
|
||||
{
|
||||
return static_cast<uint16_t>(value + Range::MID<uint16_t>);
|
||||
}
|
||||
static inline int8_t int16_to_int8(int16_t value)
|
||||
{
|
||||
return static_cast<int8_t>((value + Range::MID<uint16_t>) >> 8);
|
||||
}
|
||||
|
||||
static inline uint8_t uint16_to_uint8(uint16_t value)
|
||||
{
|
||||
return static_cast<uint8_t>(value >> 8);
|
||||
}
|
||||
static inline int16_t uint16_to_int16(uint16_t value)
|
||||
{
|
||||
return static_cast<int16_t>(value - Range::MID<uint16_t>);
|
||||
}
|
||||
static inline int8_t uint16_to_int8(uint16_t value)
|
||||
{
|
||||
return static_cast<int8_t>((value >> 8) - Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
static inline int16_t uint8_to_int16(uint8_t value)
|
||||
{
|
||||
return static_cast<int16_t>((static_cast<int32_t>(value) << 8) - Range::MID<uint16_t>);
|
||||
}
|
||||
static inline uint16_t uint8_to_uint16(uint8_t value)
|
||||
{
|
||||
return static_cast<uint16_t>(value) << 8;
|
||||
}
|
||||
static inline int8_t uint8_to_int8(uint8_t value)
|
||||
{
|
||||
return static_cast<int8_t>(value - Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
static inline int16_t int8_to_int16(int8_t value)
|
||||
{
|
||||
return static_cast<int16_t>(value) << 8;
|
||||
}
|
||||
static inline uint16_t int8_to_uint16(int8_t value)
|
||||
{
|
||||
return static_cast<uint16_t>((value + Range::MID<uint8_t>) << 8);
|
||||
}
|
||||
static inline uint8_t int8_to_uint8(int8_t value)
|
||||
{
|
||||
return static_cast<uint8_t>(value + Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
} // namespace Scale
|
||||
|
||||
#endif // _RANGE_H_
|
||||
@@ -105,28 +105,6 @@ namespace Range {
|
||||
return (value < min) ? min : (value > max) ? max : value;
|
||||
}
|
||||
|
||||
// //Scale value from one range to another, will clamp value to input range
|
||||
// template <typename To, typename From>
|
||||
// requires std::is_integral_v<To> && std::is_integral_v<From>
|
||||
// static inline To scale(From value, From min_from, From max_from, To min_to, To max_to)
|
||||
// {
|
||||
// constexpr bool is_signed_from = std::is_signed_v<From>;
|
||||
// constexpr bool is_signed_to = std::is_signed_v<To>;
|
||||
|
||||
// constexpr auto shift_from = is_signed_from ? static_cast<uint64_t>(0 - Range::MIN<From>) : 0;
|
||||
// constexpr auto shift_to = is_signed_to ? static_cast<uint64_t>(0 - Range::MIN<To>) : 0;
|
||||
|
||||
// const uint64_t u_value = static_cast<uint64_t>(value) + shift_from;
|
||||
// const uint64_t u_min_from = static_cast<uint64_t>(min_from) + shift_from;
|
||||
// const uint64_t u_max_from = static_cast<uint64_t>(max_from) + shift_from;
|
||||
// const uint64_t u_min_to = static_cast<uint64_t>(min_to) + shift_to;
|
||||
// const uint64_t u_max_to = static_cast<uint64_t>(max_to) + shift_to;
|
||||
|
||||
// const uint64_t scaled = ((u_value - u_min_from) * (u_max_to - u_min_to)) / (u_max_from - u_min_from) + u_min_to;
|
||||
|
||||
// return static_cast<To>(scaled - (is_signed_to ? shift_to : 0));
|
||||
// }
|
||||
|
||||
template <typename To, typename From>
|
||||
requires std::is_integral_v<To> && std::is_integral_v<From>
|
||||
static inline To scale(From value, From min_from, From max_from, To min_to, To max_to)
|
||||
@@ -267,41 +245,6 @@ namespace Scale //Scale and invert values
|
||||
return static_cast<uint8_t>(value + Range::MID<uint8_t>);
|
||||
}
|
||||
|
||||
// static inline uint8_t int10_to_uint8(int32_t value)
|
||||
// {
|
||||
// return Range::scale<uint8_t>(value, 10);
|
||||
// // return static_cast<uint8_t>(value >> 2);
|
||||
// }
|
||||
// static inline int16_t int10_to_int16(int32_t value)
|
||||
// {
|
||||
// return Range::scale<int16_t>(value, 10);
|
||||
// // constexpr int32_t scale_factor = Range::MAX<int16_t> - Range::MIN<int16_t>;
|
||||
// // constexpr int32_t range = INT_10::MAX - INT_10::MIN;
|
||||
|
||||
// // if (value >= INT_10::MAX)
|
||||
// // {
|
||||
// // return Range::MAX<int16_t>;
|
||||
// // }
|
||||
// // else if (value <= INT_10::MIN)
|
||||
// // {
|
||||
// // return Range::MIN<int16_t>;
|
||||
// // }
|
||||
|
||||
// // int32_t scaled_value = (value - INT_10::MIN) * scale_factor;
|
||||
// // return static_cast<int16_t>(scaled_value / range + Range::MIN<int16_t>);
|
||||
// }
|
||||
// static inline uint8_t uint10_to_uint8(int32_t value)
|
||||
// {
|
||||
// if (value > UINT_10::MAX)
|
||||
// {
|
||||
// value = UINT_10::MAX;
|
||||
// }
|
||||
// else if (value < 0)
|
||||
// {
|
||||
// value = 0;
|
||||
// }
|
||||
// return static_cast<uint8_t>(value >> 2);
|
||||
// }
|
||||
} // namespace Scale
|
||||
|
||||
#endif // _RANGE_H_
|
||||
12
README.md
12
README.md
@@ -1,9 +1,9 @@
|
||||
# OGX-Mini
|
||||

|
||||
|
||||
Firmware for the RP2040, capable of emulating gamepads for several game consoles. The firmware comes in many flavors, supported on the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), Pi Pico, Pi Pico 2, Waveshare RP2040-Zero, Pi Pico W, RP2040/ESP32 hybrid, and a 4-Channel RP2040-Zero setup.
|
||||
Firmware for the RP2040, capable of emulating gamepads for several game consoles. The firmware comes in many flavors, supported on the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723), Pi Pico, Pi Pico 2, Pi Pico W, Pi Pico 2 W, Waveshare RP2040-Zero, Pico/ESP32 hybrid, and a 4-Channel RP2040-Zero setup.
|
||||
|
||||
Visit the web app here: [**https://wiredopposite.github.io/OGX-Mini-WebApp/**](https://wiredopposite.github.io/OGX-Mini-WebApp/) to change your mappings and deadzone settings. To pair the OGX-Mini with the web app, plug your controller in, then connect it to your PC, hold **Start + Left Bumper + Right Bumper** to enter web app mode. Click "Connect" in the web app and select the OGX-Mini.
|
||||
[**Visit the web app here**](https://wiredopposite.github.io/OGX-Mini-WebApp/) to change your mappings and deadzone settings. To pair the OGX-Mini with the web app, plug your controller in, then connect it to your PC, hold **Start + Left Bumper + Right Bumper** to enter web app mode. Click "Connect" in the web app and select the OGX-Mini.
|
||||
|
||||
## Supported platforms
|
||||
- Original Xbox
|
||||
@@ -59,7 +59,7 @@ Note: There are some third party controllers that can change their VID/PID, thes
|
||||
- Most wireless adapters that present themselves as Switch/XInput/PlayStation controllers should work
|
||||
|
||||
### Wireless Bluetooth controllers (Pico W & ESP32)
|
||||
Note: Bluetooth functionality is in early testing, some may have quirks.
|
||||
**Note:** Bluetooth functionality is in early testing, some may have quirks.
|
||||
- Xbox Series, One, and Elite 2
|
||||
- Dualshock 3
|
||||
- Dualshock 4
|
||||
@@ -74,7 +74,7 @@ Please visit [**this page**](https://bluepad32.readthedocs.io/en/latest/supporte
|
||||
## Features new to v1.0.0
|
||||
- Bluetooth functionality for the Pico W and Pico+ESP32.
|
||||
- Web application (connectable via USB or Bluetooth) for configuring deadzones and buttons mappings, supports up to 8 saved profiles.
|
||||
- Pi Pico 2 (RP2350) support.
|
||||
- Pi Pico 2 and Pico 2 W (RP2350) support.
|
||||
- Reduced latency by about 3-4 ms, graphs showing comparisons are coming
|
||||
- 4 channel functionality, connect 4 Picos and use one Xbox 360 wireless adapter to control all 4.
|
||||
- Delayed USB mount until a controller is plugged in, useful for internal installation (non-Bluetooth boards only).
|
||||
@@ -101,9 +101,9 @@ For Pi Pico, RP2040-Zero, 4 channel, and ESP32 configurations, please see the ha
|
||||
|
||||
I've designed a PCB for the RP2040-Zero so you can make a small form-factor adapter yourself. The gerber files, schematic, and BOM are in Hardware folder.
|
||||
|
||||

|
||||
<img src="images/OGX-Mini-rpzero-int.jpg" alt="OGX-Mini Boards" width="400">
|
||||
|
||||
If you would like a prebuilt unit, you can purchase one, with cable and Xbox adapter included, from my [Etsy store](https://www.etsy.com/listing/1426992904/ogx-mini-controller-adapter-for-original).
|
||||
If you would like a prebuilt unit, you can purchase one, with cable and Xbox adapter included, from my [**Etsy store**](https://www.etsy.com/listing/1426992904/ogx-mini-controller-adapter-for-original).
|
||||
|
||||
## Adding supported controllers
|
||||
If your third party controller isn't working, but the original version is listed above, send me the device's VID and PID and I'll add it so it's recognized properly.
|
||||
|
||||
Reference in New Issue
Block a user