diff --git a/CMakeLists.txt b/CMakeLists.txt index 450707237..29b833e31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,7 @@ src/addons/ps4mode.cpp src/addons/reverse.cpp src/addons/turbo.cpp src/addons/slider_socd.cpp +src/addons/wiiext.cpp src/gamepad/GamepadDebouncer.cpp src/gamepad/GamepadDescriptors.cpp ) @@ -147,6 +148,7 @@ OneBitDisplay ArduinoJson rndis hardware_adc +WiiExtension pico_mbedtls TinyUSB_Gamepad ) diff --git a/docs/assets/images/gpc-add-ons-wii-extensions.png b/docs/assets/images/gpc-add-ons-wii-extensions.png new file mode 100644 index 000000000..c9f93567d Binary files /dev/null and b/docs/assets/images/gpc-add-ons-wii-extensions.png differ diff --git a/docs/web-configurator.md b/docs/web-configurator.md index 25f2a6b16..4ac507151 100644 --- a/docs/web-configurator.md +++ b/docs/web-configurator.md @@ -222,6 +222,37 @@ Enabling this add-on will allow you to use GP2040-CE on a PS4 with an 8 minute t * `Serial Number (16 Bytes in Hex Ascii)` - Choose your serial number file. * `Signature (256 Bytes in Binary)` - Choose your signature file. +### Wii Extensions + +![GP2040 Configurator - Wii Extensions](assets/images/gpc-add-ons-wii-extensions.png) + +* `I2C SDA Pin` - The GPIO pin used for Wii Extension SDA. +* `I2C SCL Pin` - The GPIO pin used for Wii Extension SCL. +* `I2C Block` - The block of I2C to use (i2c0 or i2c1). +* `I2C Speed` - Sets the speed of I2C communication. Common values are `100000` for standard, or `400000` for fast. + +Supported Extension Controllers and their mapping is as follows: + +| GP2040-CE | Nunchuck | Classic | Guitar Hero Guitar | +|-----------|----------|--------------|--------------------| +| B1 | C | B | Green | +| B2 | Z | A | Red | +| B3 | | Y | Blue | +| B4 | | X | Yellow | +| L1 | | L | | +| L2 | | ZL | | +| R1 | | R | | +| R2 | | ZR | | +| S1 | | Select | | +| S2 | | Start | | +| A1 | | Home | | +| D-Pad | | D-Pad | Strum Up/Down | +| Analog | Left | Left & Right | Left | + +Classic Controller support includes Classic, Classic Pro, and NES/SNES Mini Controllers. + +Original Classic Controller L & R triggers are analog sensitive, where Pro triggers are not. + ## Data Backup and Restoration ![GP2040-CE Configurator - Add-Ons Backup and Restore](assets/images/gpc-backup-and-restore.png) diff --git a/headers/addons/wiiext.h b/headers/addons/wiiext.h new file mode 100644 index 000000000..5fac643ac --- /dev/null +++ b/headers/addons/wiiext.h @@ -0,0 +1,85 @@ +#ifndef _WIIExtensionAddon_H +#define _WIIExtensionAddon_H + +#include +#include +#include +#include "BoardConfig.h" +#include "gpaddon.h" +#include "gamepad.h" +#include "storagemanager.h" +#include "WiiExtension.h" + +// WiiExtension Module Name +#define WiiExtensionName "WiiExtension" + +#ifndef WII_EXTENSION_ENABLED +#define WII_EXTENSION_ENABLED 1 +#endif + +#ifndef WII_EXTENSION_I2C_ADDR +#define WII_EXTENSION_I2C_ADDR 0x52 +#endif + +#ifndef WII_EXTENSION_I2C_SDA_PIN +#define WII_EXTENSION_I2C_SDA_PIN 16 +#endif + +#ifndef WII_EXTENSION_I2C_SCL_PIN +#define WII_EXTENSION_I2C_SCL_PIN 17 +#endif + +#ifndef WII_EXTENSION_I2C_BLOCK +#define WII_EXTENSION_I2C_BLOCK i2c0 +#endif + +#ifndef WII_EXTENSION_I2C_SPEED +#define WII_EXTENSION_I2C_SPEED 400000 +#endif + +class WiiExtensionInput : public GPAddon { +public: + virtual bool available(); + virtual void setup(); // WiiExtension Setup + virtual void process(); // WiiExtension Process + virtual void preprocess() {} + virtual std::string name() { return WiiExtensionName; } +private: + WiiExtension * wii; + uint32_t uIntervalMS; + uint32_t nextTimer; + + bool buttonC = false; + bool buttonZ = false; + + bool buttonA = false; + bool buttonB = false; + bool buttonX = false; + bool buttonY = false; + bool buttonL = false; + bool buttonZL = false; + bool buttonR = false; + bool buttonZR = false; + + bool buttonSelect = false; + bool buttonStart = false; + bool buttonHome = false; + + bool dpadUp = false; + bool dpadDown = false; + bool dpadLeft = false; + bool dpadRight = false; + + uint16_t triggerLeft = 0; + uint16_t triggerRight = 0; + uint16_t whammyBar = 0; + + uint16_t leftX = 0; + uint16_t leftY = 0; + uint16_t rightX = 0; + uint16_t rightY = 0; + + uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max); +}; + +#endif // _WIIExtensionAddon_H \ No newline at end of file diff --git a/headers/storagemanager.h b/headers/storagemanager.h index 61ed89374..d60df0258 100644 --- a/headers/storagemanager.h +++ b/headers/storagemanager.h @@ -140,6 +140,10 @@ struct AddonOptions { SOCDMode sliderSOCDModeOne; SOCDMode sliderSOCDModeTwo; SOCDMode sliderSOCDModeDefault; + uint8_t wiiExtensionSDAPin; + uint8_t wiiExtensionSCLPin; + int wiiExtensionBlock; + uint32_t wiiExtensionSpeed; uint8_t AnalogInputEnabled; uint8_t BoardLedAddonEnabled; uint8_t BootselButtonAddonEnabled; @@ -156,6 +160,7 @@ struct AddonOptions { uint8_t ReverseInputEnabled; uint8_t TurboInputEnabled; uint8_t SliderSOCDInputEnabled; + uint8_t WiiExtensionAddonEnabled; uint32_t checksum; }; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 467925e89..0eefad157 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -9,4 +9,5 @@ add_subdirectory(NeoPico) add_subdirectory(OneBitDisplay) add_subdirectory(PlayerLEDs) add_subdirectory(rndis) -add_subdirectory(TinyUSB_Gamepad) \ No newline at end of file +add_subdirectory(TinyUSB_Gamepad) +add_subdirectory(WiiExtension) \ No newline at end of file diff --git a/lib/WiiExtension/CMakeLists.txt b/lib/WiiExtension/CMakeLists.txt new file mode 100644 index 000000000..1048267f6 --- /dev/null +++ b/lib/WiiExtension/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(WiiExtension WiiExtension.cpp) +target_link_libraries(WiiExtension PUBLIC BitBang_I2C) +target_include_directories(WiiExtension INTERFACE .) +target_include_directories(WiiExtension PUBLIC +BitBang_I2C +) diff --git a/lib/WiiExtension/README.md b/lib/WiiExtension/README.md new file mode 100644 index 000000000..1321909f7 --- /dev/null +++ b/lib/WiiExtension/README.md @@ -0,0 +1,6 @@ +# WiiExtension +# Ported to BitBangI2C +# +# Written by: +# Mike Parks +# \ No newline at end of file diff --git a/lib/WiiExtension/WiiExtension.cpp b/lib/WiiExtension/WiiExtension.cpp new file mode 100644 index 000000000..16abe32de --- /dev/null +++ b/lib/WiiExtension/WiiExtension.cpp @@ -0,0 +1,722 @@ +#include "WiiExtension.h" + +#include +#include + +WiiExtension::WiiExtension(int sda, int scl, i2c_inst_t *i2cCtl, int32_t speed, uint8_t addr) { + iSDA = sda; + iSCL = scl; + picoI2C = i2cCtl; + bWire = bWire; + iSpeed = speed; + address = addr; +} + +void WiiExtension::begin() { + doI2CInit(); +#if WII_EXTENSION_DEBUG==true + printf("WiiExtension::begin\n"); +#endif + isReady = false; + reset(); +} + +void WiiExtension::start(){ + uint8_t idRead[32]; + uint8_t regWrite[16]; + int8_t result; + +#if WII_EXTENSION_DEBUG==true + printf("WiiExtension::start\n"); + printf("WiiExtension::start isReady? %1d\n", isReady); +#endif + + if (!isReady) return; + + regWrite[0] = 0xFA; + result = doI2CWrite(®Write[0], 1); + + extensionType = WII_EXTENSION_NONE; + + // continue if the write was successful + if (result > -1) { + doI2CRead(idRead, 6); + + if (idRead[5] == 0x00) { + extensionType = WII_EXTENSION_NUNCHUCK; + } else if (idRead[5] == 0x01) { + extensionType = WII_EXTENSION_CLASSIC; + if (idRead[0] == 0x01) { + extensionType = WII_EXTENSION_CLASSIC_PRO; + } + } else if (idRead[5] == 0x03) { + extensionType = WII_EXTENSION_GUITAR; + if (idRead[0] == 0x01) { + extensionType = WII_EXTENSION_DRUMS; + } + } else if (idRead[5] == 0x11) { + extensionType = WII_EXTENSION_TAIKO; + } + + // in certain situations (eg. Nunchuck), setting the data type in reset() does not affect what this value will be + dataType = idRead[4]; + if (dataType == WII_DATA_TYPE_0) dataType = WII_DATA_TYPE_1; + +#if WII_EXTENSION_DEBUG==true + printf("Extension ID: %02x%02x %02x%02x %02x%02x\n", idRead[0], idRead[1], idRead[2], idRead[3], idRead[4], idRead[5]); + printf("Data Format: %02x\n",idRead[4]); +#endif + + if (extensionType != WII_EXTENSION_NONE) { +#if WII_EXTENSION_DEBUG==true + //printf("Extension Type: %d\n", extensionType); +#endif + +#if WII_EXTENSION_DEBUG==true + printf("Calibration Data\n"); +#endif + if (extensionType == WII_EXTENSION_NUNCHUCK) { + _analogPrecision1From = WII_ANALOG_PRECISION_2; + _analogPrecision1To = WII_ANALOG_PRECISION_3; + +#if WII_EXTENSION_CALIBRATION==true + // read calibration + regWrite[0] = 0x20; + doI2CWrite(regWrite, 1); + + doI2CRead(idRead, 16); + + _maxX1 = idRead[8]; + _minX1 = idRead[9]; + _cenX1 = idRead[10]; + + _maxY1 = idRead[11]; + _minY1 = idRead[12]; + _cenY1 = idRead[13]; + + _accelX0G = ((idRead[0] << 2) | ((idRead[3] >> 2) & 0x03)); + _accelY0G = ((idRead[1] << 2) | ((idRead[3] >> 4) & 0x03)); + _accelZ0G = ((idRead[2] << 2) | ((idRead[3] >> 6) & 0x03)); + + _accelX1G = ((idRead[4] << 2) | ((idRead[7] >> 2) & 0x03)); + _accelY1G = ((idRead[5] << 2) | ((idRead[7] >> 4) & 0x03)); + _accelZ1G = ((idRead[6] << 2) | ((idRead[7] >> 6) & 0x03)); + +#if WII_EXTENSION_DEBUG==true + //printf("Calibration:\n"); + //printf("X0G: %d\n", _accelX0G); + //printf("Y0G: %d\n", _accelY0G); + //printf("Z0G: %d\n", _accelZ0G); + //printf("X1G: %d\n", _accelX1G); + //printf("Y1G: %d\n", _accelY1G); + //printf("YZG: %d\n", _accelZ1G); + //printf("X Min: %d\n", _minX); + //printf("X Max: %d\n", _maxX); + //printf("X Center: %d\n", _cenX); + //printf("Y Min: %d\n", _minY); + //printf("Y Max: %d\n", _maxY); + //printf("Y Center: %d\n", _cenY); +#endif +#endif + } else { + if (dataType == WII_DATA_TYPE_1) { + _analogPrecision1From = WII_ANALOG_PRECISION_1; + _analogPrecision1To = WII_ANALOG_PRECISION_3; + _analogPrecision2From = WII_ANALOG_PRECISION_0; + _analogPrecision2To = WII_ANALOG_PRECISION_3; + + _triggerPrecision1From = WII_ANALOG_PRECISION_0; + _triggerPrecision1To = WII_ANALOG_PRECISION_2; + _triggerPrecision2From = WII_ANALOG_PRECISION_0; + _triggerPrecision2To = WII_ANALOG_PRECISION_2; + } else if (dataType == WII_DATA_TYPE_2) { + _analogPrecision1From = WII_ANALOG_PRECISION_3; + _analogPrecision1To = WII_ANALOG_PRECISION_3; + _analogPrecision2From = WII_ANALOG_PRECISION_3; + _analogPrecision2To = WII_ANALOG_PRECISION_3; + + _triggerPrecision1From = WII_ANALOG_PRECISION_2; + _triggerPrecision1To = WII_ANALOG_PRECISION_2; + _triggerPrecision2From = WII_ANALOG_PRECISION_2; + _triggerPrecision2To = WII_ANALOG_PRECISION_2; + } else if (dataType == WII_DATA_TYPE_3) { + _analogPrecision1From = WII_ANALOG_PRECISION_2; + _analogPrecision1To = WII_ANALOG_PRECISION_3; + _analogPrecision2From = WII_ANALOG_PRECISION_2; + _analogPrecision2To = WII_ANALOG_PRECISION_3; + + _triggerPrecision1From = WII_ANALOG_PRECISION_2; + _triggerPrecision1To = WII_ANALOG_PRECISION_2; + _triggerPrecision2From = WII_ANALOG_PRECISION_2; + _triggerPrecision2To = WII_ANALOG_PRECISION_2; + } + +#if WII_EXTENSION_CALIBRATION==true + regWrite[0] = 0x20; + doI2CWrite(regWrite, 1); + + doI2CRead(idRead, 16); + + if (dataType == WII_DATA_TYPE_1) { + _calibrationPrecision1From = WII_ANALOG_PRECISION_2; + _calibrationPrecision1To = WII_ANALOG_PRECISION_1; + _calibrationPrecision2From = WII_ANALOG_PRECISION_2; + _calibrationPrecision2To = WII_ANALOG_PRECISION_0; + } else if (dataType == WII_DATA_TYPE_2) { + _calibrationPrecision1From = WII_ANALOG_PRECISION_2; + _calibrationPrecision1To = WII_ANALOG_PRECISION_3; + _calibrationPrecision2From = WII_ANALOG_PRECISION_2; + _calibrationPrecision2To = WII_ANALOG_PRECISION_3; + } else if (dataType == WII_DATA_TYPE_3) { + _calibrationPrecision1From = WII_ANALOG_PRECISION_2; + _calibrationPrecision1To = WII_ANALOG_PRECISION_2; + _calibrationPrecision2From = WII_ANALOG_PRECISION_2; + _calibrationPrecision2To = WII_ANALOG_PRECISION_2; + } + + _maxX1 = map(idRead[0],0,(_calibrationPrecision1From-1),0,(_calibrationPrecision1To-1)); + _minX1 = map(idRead[1],0,(_calibrationPrecision1From-1),0,(_calibrationPrecision1To-1)); + _cenX1 = map(idRead[2],0,(_calibrationPrecision1From-1),0,(_calibrationPrecision1To-1)); + + _maxY1 = map(idRead[3],0,(_calibrationPrecision1From-1),0,(_calibrationPrecision1To-1)); + _minY1 = map(idRead[4],0,(_calibrationPrecision1From-1),0,(_calibrationPrecision1To-1)); + _cenY1 = map(idRead[5],0,(_calibrationPrecision1From-1),0,(_calibrationPrecision1To-1)); + + _maxX2 = map(idRead[6],0,(_calibrationPrecision2From-1),0,(_calibrationPrecision2To-1)); + _minX2 = map(idRead[7],0,(_calibrationPrecision2From-1),0,(_calibrationPrecision2To-1)); + _cenX2 = map(idRead[8],0,(_calibrationPrecision2From-1),0,(_calibrationPrecision2To-1)); + + _maxY2 = map(idRead[9],0,(_calibrationPrecision2From-1),0,(_calibrationPrecision2To-1)); + _minY2 = map(idRead[10],0,(_calibrationPrecision2From-1),0,(_calibrationPrecision2To-1)); + _cenY2 = map(idRead[11],0,(_calibrationPrecision2From-1),0,(_calibrationPrecision2To-1)); + +#if WII_EXTENSION_DEBUG==true + printf("X1 Min: %d\n", _minX1); + printf("X1 Max: %d\n", _maxX1); + printf("X1 Center: %d\n", _cenX1); + printf("Y1 Min: %d\n", _minY1); + printf("Y1 Max: %d\n", _maxY1); + printf("Y1 Center: %d\n", _cenY1); + printf("X2 Min: %d\n", _minX2); + printf("X2 Max: %d\n", _maxX2); + printf("X2 Center: %d\n", _cenX2); + printf("Y2 Min: %d\n", _minY2); + printf("Y2 Max: %d\n", _maxY2); + printf("Y2 Center: %d\n", _cenY2); +#endif +#endif + } + + // reset to default input values in the event of a removal/hotswap + joy1X = 0; + joy1Y = 0; + joy2X = 0; + joy2Y = 0; + accelX = 0; + accelY = 0; + accelZ = 0; + + buttonZ = 0; + buttonC = 0; + buttonZR = 0; + buttonZL = 0; + buttonA = 0; + buttonB = 0; + buttonX = 0; + buttonY = 0; + buttonPlus = 0; + buttonHome = 0; + buttonMinus = 0; + buttonLT = 0; + buttonRT = 0; + + directionUp = 0; + directionDown = 0; + directionLeft = 0; + directionRight = 0; + + triggerLeft = 0; + triggerRight = 0; + + fretGreen = 0; + fretRed = 0; + fretYellow = 0; + fretBlue = 0; + fretOrange = 0; + pedalButton = 0; + + rimLeft = 0; + rimRight = 0; + drumLeft = 0; + drumRight = 0; + + whammyBar = 0; + } else { +#if WII_EXTENSION_DEBUG==true + printf("Unknown Extension: %02x%02x %02x%02x %02x%02x\n", idRead[0], idRead[1], idRead[2], idRead[3], idRead[4], idRead[5]); +#endif + } + + regWrite[0] = 0x00; + result = doI2CWrite(regWrite, 1); + } +} + +void WiiExtension::reset(){ + uint8_t regWrite[16]; + int8_t result; + bool canContinue = true; + + if (canContinue) { + result = doI2CTest(); + canContinue = (result == 1); + } + +#if WII_EXTENSION_DEBUG==true + printf("WiiExtension::reset\n"); +#endif + + if (canContinue) { + regWrite[0] = 0xF0; + regWrite[1] = 0x55; + result = doI2CWrite(regWrite, 2); + canContinue = (result > -1); + } + + if (canContinue) { + regWrite[0] = 0xFB; + regWrite[1] = 0x00; + result = doI2CWrite(regWrite, 2); + canContinue = (result > -1); + } + + if (canContinue) { + // set data format + regWrite[0] = 0xFE; + regWrite[1] = 0x03; + result = doI2CWrite(regWrite, 2); + canContinue = (result > -1); + } + +#if WII_EXTENSION_DEBUG==true + printf("WiiExtension::reset canContinue? %1d\n", canContinue); +#endif + + if (canContinue) { +#if WII_EXTENSION_DEBUG==true + //printf("Reset Sent\n"); +#endif + isReady = true; + } else { +#if WII_EXTENSION_DEBUG==true + //printf("Device not found\n"); +#endif + } +} + +void WiiExtension::poll() { + uint8_t reg = _u(0x08); + uint8_t regWrite[16]; + uint8_t regRead[16]; + int8_t result; + +#if WII_EXTENSION_DEBUG==true + //printf("WiiExtension::poll\n"); + //printf("WiiExtension::poll isReady? %1d\n", isReady); +#endif + + if (!isReady) return; + + if (extensionType != WII_EXTENSION_NONE) { + switch (dataType) { + case WII_DATA_TYPE_1: + result = doI2CRead(regRead, 6); + break; + case WII_DATA_TYPE_2: + result = doI2CRead(regRead, 9); + break; + case WII_DATA_TYPE_3: + result = doI2CRead(regRead, 8); + break; + default: + // unknown. TBD + result = -1; +#if WII_EXTENSION_DEBUG==true + printf("WiiExtension::poll Unknown data type: %1d\n", dataType); +#endif + break; + } + + if (result > 0) { + switch (extensionType) { + case WII_EXTENSION_NUNCHUCK: + joy1X = (regRead[0] & 0xFF); + joy1Y = (regRead[1] & 0xFF); + + accelX = (((regRead[2] << 2) | ((regRead[5] >> 2) & 0x03))); + accelY = (((regRead[3] << 2) | ((regRead[5] >> 4) & 0x03))); + accelZ = (((regRead[4] << 2) | ((regRead[5] >> 6) & 0x03))); + buttonZ = (!(regRead[5] & 0x01)); + buttonC = (!(regRead[5] & 0x02)); + +#if WII_EXTENSION_DEBUG==true + printf("Joy X=%4d Y=%4d Acc X=%4d Y=%4d Z=%4d Btn Z=%1d C=%1d\n", joy1X, joy1Y, accelX, accelY, accelZ, buttonZ, buttonC); +#endif + + break; + case WII_EXTENSION_CLASSIC: + case WII_EXTENSION_CLASSIC_PRO: + // write data format to return + // see wiki for data types + if (dataType == WII_DATA_TYPE_1) { + joy1X = (regRead[0] & 0x3F); + joy1Y = (regRead[1] & 0x3F); + joy2X = ((regRead[0] & 0xC0) >> 3) | ((regRead[1] & 0xC0) >> 5) | ((regRead[2] & 0x80) >> 7); + joy2Y = (regRead[2] & 0x1F); + + triggerLeft = (((regRead[2] & 0x60) >> 2) | ((regRead[3] & 0xE0) >> 5)); + triggerRight = ((regRead[3] & 0x1F) >> 0); + + directionRight = !((regRead[4] & 0x80) >> 7); + directionDown = !((regRead[4] & 0x40) >> 6); + buttonLT = !((regRead[4] & 0x20) >> 5); + buttonMinus = !((regRead[4] & 0x10) >> 4); + buttonHome = !((regRead[4] & 0x08) >> 3); + buttonPlus = !((regRead[4] & 0x04) >> 2); + buttonRT = !((regRead[4] & 0x02) >> 1); + + buttonZL = !((regRead[5] & 0x80) >> 7); + buttonB = !((regRead[5] & 0x40) >> 6); + buttonY = !((regRead[5] & 0x20) >> 5); + buttonA = !((regRead[5] & 0x10) >> 4); + buttonX = !((regRead[5] & 0x08) >> 3); + buttonZR = !((regRead[5] & 0x04) >> 2); + directionLeft = !((regRead[5] & 0x02) >> 1); + directionUp = !((regRead[5] & 0x01) >> 0); + } else if (dataType == WII_DATA_TYPE_2) { + joy1X = ((regRead[0] << 2) | ((regRead[4] & 0x03) >> 0)); + joy1Y = ((regRead[2] << 2) | ((regRead[4] & 0x30) >> 4)); + joy2X = ((regRead[1] << 2) | ((regRead[4] & 0x0C) >> 2)); + joy2Y = ((regRead[3] << 2) | ((regRead[4] & 0xC0) >> 6)); + + triggerLeft = (regRead[5] & 0xFF); + triggerRight = (regRead[6] & 0xFF); + + directionRight = !((regRead[7] & 0x80) >> 7); + directionDown = !((regRead[7] & 0x40) >> 6); + buttonLT = !((regRead[7] & 0x20) >> 5); + buttonMinus = !((regRead[7] & 0x10) >> 4); + buttonHome = !((regRead[7] & 0x08) >> 3); + buttonPlus = !((regRead[7] & 0x04) >> 2); + buttonRT = !((regRead[7] & 0x02) >> 1); + + buttonZL = !((regRead[8] & 0x80) >> 7); + buttonB = !((regRead[8] & 0x40) >> 6); + buttonY = !((regRead[8] & 0x20) >> 5); + buttonA = !((regRead[8] & 0x10) >> 4); + buttonX = !((regRead[8] & 0x08) >> 3); + buttonZR = !((regRead[8] & 0x04) >> 2); + directionLeft = !((regRead[8] & 0x02) >> 1); + directionUp = !((regRead[8] & 0x01) >> 0); + } else if (dataType == WII_DATA_TYPE_3) { + joy1X = (regRead[0] & 0xFF); + joy1Y = (regRead[2] & 0xFF); + joy2X = (regRead[1] & 0xFF); + joy2Y = (regRead[3] & 0xFF); + + triggerLeft = (regRead[4] & 0xFF); + triggerRight = (regRead[5] & 0xFF); + + directionRight = !((regRead[6] & 0x80) >> 7); + directionDown = !((regRead[6] & 0x40) >> 6); + buttonLT = !((regRead[6] & 0x20) >> 5); + buttonMinus = !((regRead[6] & 0x10) >> 4); + buttonHome = !((regRead[6] & 0x08) >> 3); + buttonPlus = !((regRead[6] & 0x04) >> 2); + buttonRT = !((regRead[6] & 0x02) >> 1); + + buttonZL = !((regRead[7] & 0x80) >> 7); + buttonB = !((regRead[7] & 0x40) >> 6); + buttonY = !((regRead[7] & 0x20) >> 5); + buttonA = !((regRead[7] & 0x10) >> 4); + buttonX = !((regRead[7] & 0x08) >> 3); + buttonZR = !((regRead[7] & 0x04) >> 2); + directionLeft = !((regRead[7] & 0x02) >> 1); + directionUp = !((regRead[7] & 0x01) >> 0); + } else { + // unknown + } + +#if WII_EXTENSION_DEBUG==true + //if ((_lastRead[0] != regRead[0]) || (_lastRead[1] != regRead[1]) || (_lastRead[2] != regRead[2]) || (_lastRead[3] != regRead[3])) { + printf("Joy1 X=%4d Y=%4d Joy2 X=%4d Y=%4d\n", joy1X, joy1Y, joy2X, joy2Y); + //} + //printf("Joy1 X=%4d Y=%4d Joy2 X=%4d Y=%4d U=%1d D=%1d L=%1d R=%1d TL=%4d TR=%4d\n", joy1X, joy1Y, joy2X, joy2Y, directionUp, directionDown, directionLeft, directionRight, triggerLeft, triggerRight); + //printf("A=%1d B=%1d X=%1d Y=%1d ZL=%1d ZR=%1d LT=%1d RT=%1d -=%1d H=%1d +=%1d\n", buttonA, buttonB, buttonX, buttonY, buttonZL, buttonZR, buttonLT, buttonRT, buttonMinus, buttonHome, buttonPlus); +#endif + + break; + case WII_EXTENSION_GUITAR: + if (dataType == WII_DATA_TYPE_1) { + joy1X = (regRead[0] & 0x3F); + joy1Y = (regRead[1] & 0x3F); + + whammyBar = (regRead[3] & 0x1F); + joy2X = (regRead[3] & 0x1F); + + directionDown = !((regRead[4] & 0x40) >> 6); + buttonMinus = !((regRead[4] & 0x10) >> 4); + buttonPlus = !((regRead[4] & 0x04) >> 2); + + fretOrange = !((regRead[5] & 0x80) >> 7); + fretRed = !((regRead[5] & 0x40) >> 6); + fretBlue = !((regRead[5] & 0x20) >> 5); + fretGreen = !((regRead[5] & 0x10) >> 4); + fretYellow = !((regRead[5] & 0x08) >> 3); + pedalButton = !((regRead[5] & 0x04) >> 2); + directionUp = !((regRead[5] & 0x01) >> 0); + } else if (dataType == WII_DATA_TYPE_2) { + joy1X = ((regRead[0] << 2) | ((regRead[4] & 0x03) >> 0)); + joy1Y = ((regRead[2] << 2) | ((regRead[4] & 0x30) >> 4)); + + // analog 2 is not used but appears to not change? + //joy2X = ((regRead[1] << 2) | ((regRead[4] & 0x0C) >> 2)); + //joy2Y = ((regRead[3] << 2) | ((regRead[4] & 0xC0) >> 6)); + + whammyBar = (regRead[6] & 0xFF); + joy2X = (regRead[6] & 0xFF); + + directionDown = !((regRead[7] & 0x40) >> 6); + buttonMinus = !((regRead[7] & 0x10) >> 4); + buttonPlus = !((regRead[7] & 0x04) >> 2); + + fretOrange = !((regRead[8] & 0x80) >> 7); + fretRed = !((regRead[8] & 0x40) >> 6); + fretBlue = !((regRead[8] & 0x20) >> 5); + fretGreen = !((regRead[8] & 0x10) >> 4); + fretYellow = !((regRead[8] & 0x08) >> 3); + pedalButton = !((regRead[8] & 0x04) >> 2); + directionUp = !((regRead[8] & 0x01) >> 0); + } else if (dataType == WII_DATA_TYPE_3) { + joy1X = (regRead[0] & 0xFF); + joy1Y = (regRead[2] & 0xFF); + + // analog 2 is not used but appears to not change? + //joy2X = regRead[1]; + //joy2Y = regRead[3]; + + whammyBar = (regRead[5] & 0xFF); + joy2X = (regRead[5] & 0xFF); + + directionDown = !((regRead[6] & 0x40) >> 6); + buttonMinus = !((regRead[6] & 0x10) >> 4); + buttonPlus = !((regRead[6] & 0x04) >> 2); + + fretOrange = !((regRead[7] & 0x80) >> 7); + fretRed = !((regRead[7] & 0x40) >> 6); + fretBlue = !((regRead[7] & 0x20) >> 5); + fretGreen = !((regRead[7] & 0x10) >> 4); + fretYellow = !((regRead[7] & 0x08) >> 3); + pedalButton = !((regRead[7] & 0x04) >> 2); + directionUp = !((regRead[7] & 0x01) >> 0); + } + +#if WII_EXTENSION_DEBUG==true +// printf("Joy1 X=%4d Y=%4d Whammy=%4d U=%1d D=%1d -=%1d +=%1d\n", joy1X, joy1Y, whammyBar, directionUp, directionDown, buttonMinus, buttonPlus); +// printf("Joy1 X=%4d Y=%4d Whammy=%4d U=%1d D=%1d -=%1d +=%1d\n", joy1X, joy1Y, whammyBar, directionUp, directionDown, buttonMinus, buttonPlus); +// printf("O=%1d B=%1d Y=%1d R=%1d G=%1d\n", fretOrange, fretBlue, fretYellow, fretRed, fretGreen); +#endif + break; + case WII_EXTENSION_TAIKO: + if (dataType == WII_DATA_TYPE_1) { + drumLeft = !((regRead[5] & 0x40) >> 6); + rimLeft = !((regRead[5] & 0x20) >> 5); + drumRight = !((regRead[5] & 0x10) >> 4); + rimRight = !((regRead[5] & 0x08) >> 3); + } else if (dataType == WII_DATA_TYPE_2) { + drumLeft = !((regRead[8] & 0x40) >> 6); + rimLeft = !((regRead[8] & 0x20) >> 5); + drumRight = !((regRead[8] & 0x10) >> 4); + rimRight = !((regRead[8] & 0x08) >> 3); + } else if (dataType == WII_DATA_TYPE_3) { + drumLeft = !((regRead[7] & 0x40) >> 6); + rimLeft = !((regRead[7] & 0x20) >> 5); + drumRight = !((regRead[7] & 0x10) >> 4); + rimRight = !((regRead[7] & 0x08) >> 3); + } + +#if WII_EXTENSION_DEBUG==true + //if (_lastRead[0] != regRead[0]) printf("Byte0 " BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(regRead[0])); + //if (_lastRead[1] != regRead[1]) printf("Byte1 " BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(regRead[1])); + //if (_lastRead[2] != regRead[2]) printf("Byte2 " BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(regRead[2])); + //if (_lastRead[3] != regRead[3]) printf("Byte3 " BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(regRead[3])); + //if (_lastRead[4] != regRead[4]) printf("Byte4 " BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(regRead[4])); + //if (_lastRead[5] != regRead[5]) printf("Byte5 " BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(regRead[5])); + // + //if (_lastRead[7] != regRead[7]) { + // printf("DL=%1d RL=%1d DR=%1d RR=%1d\n", drumLeft, rimLeft, drumRight, rimRight); + //} +#endif + + break; + } + + // calibrate and remap + joy1X = map( + calibrate(joy1X, _minX1, _maxX1, _cenX1), + 0+_minX1, + (_analogPrecision1From-_maxX1), + 0, + (_analogPrecision1To-1) + ); + joy1Y = map( + calibrate(joy1Y, _minY1, _maxY1, _cenY1), + 0+_minY1, + (_analogPrecision1From-_maxY1), + 0, + (_analogPrecision1To-1) + ); + + joy2X = map( + calibrate(joy2X, _minX2, _maxX2, _cenX2), + 0+_minX2, + (_analogPrecision2From-_maxX2), + 0, + (_analogPrecision2To-1) + ); + joy2Y = map( + calibrate(joy2Y, _minY2, _maxY2, _cenY2), + 0+_minY2, + (_analogPrecision2From-_maxY2), + 0, + (_analogPrecision2To-1) + ); + + triggerLeft = map( + triggerLeft, + 0, + (_triggerPrecision1From-1), + 0, + (_triggerPrecision1To-1) + ); + triggerRight = map( + triggerRight, + 0, + (_triggerPrecision2From-1), + 0, + (_triggerPrecision2To-1) + ); + +#if WII_EXTENSION_DEBUG==true + //if ((_lastRead[0] != regRead[0]) || (_lastRead[1] != regRead[1]) || (_lastRead[2] != regRead[2]) || (_lastRead[3] != regRead[3])) { + // printf("Joy1 X=%4d Y=%4d Joy2 X=%4d Y=%4d\n", joy1X, joy1Y, joy2X, joy2Y); + //} + //printf("Joy1 X=%4d Y=%4d Joy2 X=%4d Y=%4d U=%1d D=%1d L=%1d R=%1d TL=%4d TR=%4d\n", joy1X, joy1Y, joy2X, joy2Y, directionUp, directionDown, directionLeft, directionRight, triggerLeft, triggerRight); + //printf("A=%1d B=%1d X=%1d Y=%1d ZL=%1d ZR=%1d LT=%1d RT=%1d -=%1d H=%1d +=%1d\n", buttonA, buttonB, buttonX, buttonY, buttonZL, buttonZR, buttonLT, buttonRT, buttonMinus, buttonHome, buttonPlus); + for (int i = 0; i < result; ++i) { + _lastRead[i] = regRead[i]; + } +#endif + // continue poll + regWrite[0] = 0x00; + result = doI2CWrite(regWrite, 1); + } else { + // device disconnected or invalid read + extensionType = WII_EXTENSION_NONE; + reset(); + start(); + } + } else { + reset(); + start(); + } +} + +uint16_t WiiExtension::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; +} + +uint16_t WiiExtension::calibrate(uint16_t pos, uint16_t min, uint16_t max, uint16_t cen) { + uint16_t result; + +#if WII_EXTENSION_CALIBRATION==true + if (pos >= min && pos <= max) { + result = pos; + } else { + if (pos < min) { + result = min; + } else if (pos > max) { + result = max; + } else { + result = cen; + } + } +#else + result = pos; +#endif + + return result; +} + +int WiiExtension::doI2CWrite(uint8_t *pData, int iLen) { + int result = i2c_write_blocking(picoI2C, address, pData, iLen, false); + waitUntil_us(WII_EXTENSION_DELAY); + return result; +} + +int WiiExtension::doI2CRead(uint8_t *pData, int iLen) { + int result = i2c_read_blocking(picoI2C, address, pData, iLen, false); + waitUntil_us(WII_EXTENSION_DELAY); + return result; +} + +uint8_t WiiExtension::doI2CTest() { + int result; + uint8_t rxdata; + result = doI2CRead(&rxdata, 1); + return (result >= 0); +} + +void WiiExtension::doI2CInit() { + if ((iSDA + 2 * i2c_hw_index(picoI2C))%4 != 0) return; + if ((iSCL + 3 + 2 * i2c_hw_index(picoI2C))%4 != 0) return; + + i2c_init(picoI2C, iSpeed); + gpio_set_function(iSDA, GPIO_FUNC_I2C); + gpio_set_function(iSCL, GPIO_FUNC_I2C); + gpio_pull_up(iSDA); + gpio_pull_up(iSCL); + + return; +} + +void WiiExtension::waitUntil_us(uint64_t us) { + WiiExtension_alarmFired = false; + + // Enable the interrupt for our alarm (the timer outputs 4 alarm irqs) + hw_set_bits(&timer_hw->inte, 1u << WII_ALARM_NUM); + // Set irq handler for alarm irq + irq_set_exclusive_handler(WII_ALARM_IRQ, alarmIRQ); + // Enable the alarm irq + irq_set_enabled(WII_ALARM_IRQ, true); + // Enable interrupt in block and at processor + + // Alarm is only 32 bits so if trying to delay more + // than that need to be careful and keep track of the upper + // bits + uint64_t target = timer_hw->timerawl + us; + + // Write the lower 32 bits of the target time to the alarm which + // will arm it + timer_hw->alarm[WII_ALARM_NUM] = (uint32_t) target; + + while (!WiiExtension_alarmFired); +} + +void WiiExtension::alarmIRQ() { + // Clear the alarm irq + hw_clear_bits(&timer_hw->intr, 1u << WII_ALARM_NUM); + + // Assume alarm 0 has fired + WiiExtension_alarmFired = true; +} diff --git a/lib/WiiExtension/WiiExtension.h b/lib/WiiExtension/WiiExtension.h new file mode 100644 index 000000000..a869afd54 --- /dev/null +++ b/lib/WiiExtension/WiiExtension.h @@ -0,0 +1,187 @@ +// WiiNunchuk Library +// category=Signal Input/Output + +#ifndef _WIIEXTENSION_H_ +#define _WIIEXTENSION_H_ + +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +#define WII_EXTENSION_NONE -1 +#define WII_EXTENSION_NUNCHUCK 0 +#define WII_EXTENSION_CLASSIC 1 +#define WII_EXTENSION_CLASSIC_PRO 2 +#define WII_EXTENSION_DRAWSOME 3 +#define WII_EXTENSION_GUITAR 4 +#define WII_EXTENSION_DRUMS 5 +#define WII_EXTENSION_TURNTABLE 6 +#define WII_EXTENSION_TAIKO 7 +#define WII_EXTENSION_UDRAW 8 +#define WII_EXTENSION_BALANCE_BOARD 9 +#define WII_EXTENSION_MOTION_PLUS 10 + +#define WII_DATA_TYPE_0 0 +#define WII_DATA_TYPE_1 1 +#define WII_DATA_TYPE_2 2 +#define WII_DATA_TYPE_3 3 + +#define WII_ANALOG_PRECISION_0 32 +#define WII_ANALOG_PRECISION_1 64 +#define WII_ANALOG_PRECISION_2 256 +#define WII_ANALOG_PRECISION_3 1024 + +#ifndef WII_EXTENSION_DEBUG +#define WII_EXTENSION_DEBUG false +#endif + +#ifndef WII_EXTENSION_DELAY +#define WII_EXTENSION_DELAY 300 +#endif + +#ifndef WII_EXTENSION_TIMEOUT +#define WII_EXTENSION_TIMEOUT 2 +#endif + +#ifndef WII_EXTENSION_CALIBRATION +#define WII_EXTENSION_CALIBRATION false +#endif + +#define WII_ALARM_NUM 0 +#define WII_ALARM_IRQ TIMER_IRQ_0 + +static volatile bool WiiExtension_alarmFired; + +#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +#define BYTE_TO_BINARY(byte) \ + ((byte) & 0x80 ? '1' : '0'), \ + ((byte) & 0x40 ? '1' : '0'), \ + ((byte) & 0x20 ? '1' : '0'), \ + ((byte) & 0x10 ? '1' : '0'), \ + ((byte) & 0x08 ? '1' : '0'), \ + ((byte) & 0x04 ? '1' : '0'), \ + ((byte) & 0x02 ? '1' : '0'), \ + ((byte) & 0x01 ? '1' : '0') + +class WiiExtension { + protected: + uint8_t address; + public: + int8_t extensionType = WII_EXTENSION_NONE; + int8_t dataType = WII_DATA_TYPE_0; + + uint16_t joy1X = 0; + uint16_t joy1Y = 0; + uint16_t joy2X = 0; + uint16_t joy2Y = 0; + uint16_t accelX = 0; + uint16_t accelY = 0; + uint16_t accelZ = 0; + + bool buttonZ = false; + bool buttonC = false; + bool buttonZR = false; + bool buttonZL = false; + bool buttonA = false; + bool buttonB = false; + bool buttonX = false; + bool buttonY = false; + bool buttonPlus = false; + bool buttonHome = false; + bool buttonMinus = false; + bool buttonLT = false; + bool buttonRT = false; + + bool directionUp = false; + bool directionDown = false; + bool directionLeft = false; + bool directionRight = false; + + uint16_t triggerLeft = 0; + uint16_t triggerRight = 0; + + bool fretGreen = false; + bool fretRed = false; + bool fretYellow = false; + bool fretBlue = false; + bool fretOrange = false; + bool pedalButton = false; + + uint16_t whammyBar = 0; + + bool rimLeft = false; + bool rimRight = false; + bool drumLeft = false; + bool drumRight = false; + + bool isReady = false; + + // Constructor + WiiExtension(int sda, int scl, i2c_inst_t *i2cCtl, int32_t speed, uint8_t addr); + + // Methods + void begin(); + void reset(); + void start(); + void poll(); + private: + + uint8_t iSDA; + uint8_t iSCL; + uint8_t bWire; + i2c_inst_t *picoI2C; + + int32_t iSpeed; + + uint16_t _minX1 = 0; + uint16_t _maxX1 = 1; + uint16_t _cenX1 = 0; + + uint16_t _minY1 = 0; + uint16_t _maxY1 = 1; + uint16_t _cenY1 = 0; + + uint16_t _minX2 = 0; + uint16_t _maxX2 = 1; + uint16_t _cenX2 = 0; + + uint16_t _minY2 = 0; + uint16_t _maxY2 = 1; + uint16_t _cenY2 = 0; + + uint16_t _accelX0G = 0; + uint16_t _accelY0G = 0; + uint16_t _accelZ0G = 0; + uint16_t _accelX1G = 0; + uint16_t _accelY1G = 0; + uint16_t _accelZ1G = 0; + + //uint8_t _lastRead[16]; + + uint16_t _calibrationPrecision1From = WII_ANALOG_PRECISION_0; + uint16_t _calibrationPrecision1To = WII_ANALOG_PRECISION_0; + uint16_t _calibrationPrecision2From = WII_ANALOG_PRECISION_0; + uint16_t _calibrationPrecision2To = WII_ANALOG_PRECISION_0; + + uint16_t _analogPrecision1From = WII_ANALOG_PRECISION_0; + uint16_t _analogPrecision1To = WII_ANALOG_PRECISION_0; + uint16_t _analogPrecision2From = WII_ANALOG_PRECISION_0; + uint16_t _analogPrecision2To = WII_ANALOG_PRECISION_0; + + uint16_t _triggerPrecision1From = WII_ANALOG_PRECISION_0; + uint16_t _triggerPrecision1To = WII_ANALOG_PRECISION_0; + uint16_t _triggerPrecision2From = WII_ANALOG_PRECISION_0; + uint16_t _triggerPrecision2To = WII_ANALOG_PRECISION_0; + + uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max); + uint16_t calibrate(uint16_t pos, uint16_t min, uint16_t max, uint16_t center); + + int doI2CWrite(uint8_t *pData, int iLen); + int doI2CRead(uint8_t *pData, int iLen); + uint8_t doI2CTest(); + void doI2CInit(); + + void waitUntil_us(uint64_t us); + static void alarmIRQ(); +}; + +#endif diff --git a/src/addons/wiiext.cpp b/src/addons/wiiext.cpp new file mode 100644 index 000000000..a40aa104a --- /dev/null +++ b/src/addons/wiiext.cpp @@ -0,0 +1,146 @@ +#include "addons/wiiext.h" +#include "storagemanager.h" +#include "hardware/gpio.h" + +bool WiiExtensionInput::available() { + const BoardOptions& boardOptions = Storage::getInstance().getBoardOptions(); + AddonOptions options = Storage::getInstance().getAddonOptions(); + + return (!boardOptions.hasI2CDisplay && (options.WiiExtensionAddonEnabled && + options.wiiExtensionSDAPin != (uint8_t)-1 && + options.wiiExtensionSCLPin != (uint8_t)-1)); +} + +void WiiExtensionInput::setup() { + AddonOptions options = Storage::getInstance().getAddonOptions(); + nextTimer = getMillis(); + +#if WII_EXTENSION_DEBUG==true + stdio_init_all(); +#endif + + uIntervalMS = 0; + + wii = new WiiExtension( + options.wiiExtensionSDAPin, + options.wiiExtensionSCLPin, + options.wiiExtensionBlock == 0 ? i2c0 : i2c1, + options.wiiExtensionSpeed, + WII_EXTENSION_I2C_ADDR); + wii->begin(); + wii->start(); +} + +void WiiExtensionInput::process() { + if (nextTimer < getMillis()) { + wii->poll(); + + if (wii->extensionType == WII_EXTENSION_NUNCHUCK) { + buttonZ = wii->buttonZ; + buttonC = wii->buttonC; + + leftX = map(wii->joy1X,0,1023,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + leftY = map(wii->joy1Y,1023,0,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + rightX = GAMEPAD_JOYSTICK_MID; + rightY = GAMEPAD_JOYSTICK_MID; + + triggerLeft = 0; + triggerRight = 0; + } else if ((wii->extensionType == WII_EXTENSION_CLASSIC) || (wii->extensionType == WII_EXTENSION_CLASSIC_PRO)) { + buttonA = wii->buttonA; + buttonB = wii->buttonB; + buttonX = wii->buttonX; + buttonY = wii->buttonY; + buttonL = wii->buttonZL; + buttonZL = wii->buttonLT; + buttonR = wii->buttonZR; + buttonZR = wii->buttonRT; + dpadUp = wii->directionUp; + dpadDown = wii->directionDown; + dpadLeft = wii->directionLeft; + dpadRight = wii->directionRight; + buttonSelect = wii->buttonMinus; + buttonStart = wii->buttonPlus; + buttonHome = wii->buttonHome; + + if (wii->extensionType == WII_EXTENSION_CLASSIC) { + triggerLeft = wii->triggerLeft; + triggerRight = wii->triggerRight; + } + + leftX = map(wii->joy1X,0,WII_ANALOG_PRECISION_3,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + leftY = map(wii->joy1Y,WII_ANALOG_PRECISION_3,0,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + rightX = map(wii->joy2X,0,WII_ANALOG_PRECISION_3,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + rightY = map(wii->joy2Y,WII_ANALOG_PRECISION_3,0,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + } else if (wii->extensionType == WII_EXTENSION_GUITAR) { + buttonSelect = wii->buttonMinus; + buttonStart = wii->buttonPlus; + + dpadUp = wii->directionUp; + dpadDown = wii->directionDown; + + buttonB = wii->fretGreen; + buttonA = wii->fretRed; + buttonX = wii->fretYellow; + buttonY = wii->fretBlue; + buttonL = wii->fretOrange; + + // whammy currently maps to Joy2X in addition to the raw whammy value + whammyBar = wii->whammyBar; + + leftX = map(wii->joy1X,0,WII_ANALOG_PRECISION_3,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + leftY = map(wii->joy1Y,WII_ANALOG_PRECISION_3,0,GAMEPAD_JOYSTICK_MIN,GAMEPAD_JOYSTICK_MAX); + rightX = map(wii->joy2X,0,WII_ANALOG_PRECISION_3,GAMEPAD_JOYSTICK_MID,GAMEPAD_JOYSTICK_MAX); + rightY = GAMEPAD_JOYSTICK_MID; + + triggerLeft = 0; + triggerRight = 0; + } else if (wii->extensionType == WII_EXTENSION_TAIKO) { + buttonL = wii->rimLeft; + buttonR = wii->rimRight; + + dpadRight = wii->drumLeft; + buttonA = wii->drumRight; + } + + nextTimer = getMillis() + uIntervalMS; + } + + Gamepad * gamepad = Storage::getInstance().GetGamepad(); + + gamepad->state.lx = leftX; + gamepad->state.ly = leftY; + gamepad->state.rx = rightX; + gamepad->state.ry = rightY; + + if (wii->extensionType == WII_EXTENSION_CLASSIC) { + gamepad->hasAnalogTriggers = true; + gamepad->state.lt = triggerLeft; + gamepad->state.rt = triggerRight; + } else { + gamepad->hasAnalogTriggers = false; + } + + if (buttonC) gamepad->state.buttons |= GAMEPAD_MASK_B1; + if (buttonZ) gamepad->state.buttons |= GAMEPAD_MASK_B2; + + if (buttonA) gamepad->state.buttons |= GAMEPAD_MASK_B2; + if (buttonB) gamepad->state.buttons |= GAMEPAD_MASK_B1; + if (buttonX) gamepad->state.buttons |= GAMEPAD_MASK_B4; + if (buttonY) gamepad->state.buttons |= GAMEPAD_MASK_B3; + if (buttonL) gamepad->state.buttons |= GAMEPAD_MASK_L1; + if (buttonZL) gamepad->state.buttons |= GAMEPAD_MASK_L2; + if (buttonR) gamepad->state.buttons |= GAMEPAD_MASK_R1; + if (buttonZR) gamepad->state.buttons |= GAMEPAD_MASK_R2; + if (buttonSelect) gamepad->state.buttons |= GAMEPAD_MASK_S1; + if (buttonStart) gamepad->state.buttons |= GAMEPAD_MASK_S2; + if (buttonHome) gamepad->state.buttons |= GAMEPAD_MASK_A1; + if (dpadUp) gamepad->state.dpad |= GAMEPAD_MASK_UP; + if (dpadDown) gamepad->state.dpad |= GAMEPAD_MASK_DOWN; + if (dpadLeft) gamepad->state.dpad |= GAMEPAD_MASK_LEFT; + if (dpadRight) gamepad->state.dpad |= GAMEPAD_MASK_RIGHT; +} + +uint16_t WiiExtensionInput::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; +} diff --git a/src/configs/webconfig.cpp b/src/configs/webconfig.cpp index 1c8d0d391..5b2bdb260 100644 --- a/src/configs/webconfig.cpp +++ b/src/configs/webconfig.cpp @@ -802,6 +802,10 @@ std::string setAddonOptions() docToValue(addonOptions.sliderSOCDModeOne, doc, "sliderSOCDModeOne"); docToValue(addonOptions.sliderSOCDModeTwo, doc, "sliderSOCDModeTwo"); docToValue(addonOptions.sliderSOCDModeDefault, doc, "sliderSOCDModeDefault"); + docToPin(addonOptions.wiiExtensionSDAPin, doc, "wiiExtensionSDAPin"); + docToPin(addonOptions.wiiExtensionSCLPin, doc, "wiiExtensionSCLPin"); + docToValue(addonOptions.wiiExtensionBlock, doc, "wiiExtensionBlock"); + docToValue(addonOptions.wiiExtensionSpeed, doc, "wiiExtensionSpeed"); docToValue(addonOptions.AnalogInputEnabled, doc, "AnalogInputEnabled"); docToValue(addonOptions.BoardLedAddonEnabled, doc, "BoardLedAddonEnabled"); docToValue(addonOptions.BuzzerSpeakerAddonEnabled, doc, "BuzzerSpeakerAddonEnabled"); @@ -815,6 +819,7 @@ std::string setAddonOptions() docToValue(addonOptions.PS4ModeAddonEnabled, doc, "PS4ModeAddonEnabled"); docToValue(addonOptions.ReverseInputEnabled, doc, "ReverseInputEnabled"); docToValue(addonOptions.TurboInputEnabled, doc, "TurboInputEnabled"); + docToValue(addonOptions.WiiExtensionAddonEnabled, doc, "WiiExtensionAddonEnabled"); Storage::getInstance().setAddonOptions(addonOptions); @@ -972,6 +977,10 @@ std::string getAddonOptions() writeDoc(doc, "sliderSOCDModeOne", addonOptions.sliderSOCDModeOne); writeDoc(doc, "sliderSOCDModeTwo", addonOptions.sliderSOCDModeTwo); writeDoc(doc, "sliderSOCDModeDefault", addonOptions.sliderSOCDModeDefault); + writeDoc(doc, "wiiExtensionSDAPin", addonOptions.wiiExtensionSDAPin == 0xFF ? -1 : addonOptions.wiiExtensionSDAPin); + writeDoc(doc, "wiiExtensionSCLPin", addonOptions.wiiExtensionSCLPin == 0xFF ? -1 : addonOptions.wiiExtensionSCLPin); + writeDoc(doc, "wiiExtensionBlock", addonOptions.wiiExtensionBlock); + writeDoc(doc, "wiiExtensionSpeed", addonOptions.wiiExtensionSpeed); writeDoc(doc, "AnalogInputEnabled", addonOptions.AnalogInputEnabled); writeDoc(doc, "BoardLedAddonEnabled", addonOptions.BoardLedAddonEnabled); writeDoc(doc, "BuzzerSpeakerAddonEnabled", addonOptions.BuzzerSpeakerAddonEnabled); @@ -985,6 +994,7 @@ std::string getAddonOptions() writeDoc(doc, "PS4ModeAddonEnabled", addonOptions.PS4ModeAddonEnabled); writeDoc(doc, "ReverseInputEnabled", addonOptions.ReverseInputEnabled); writeDoc(doc, "TurboInputEnabled", addonOptions.TurboInputEnabled); + writeDoc(doc, "WiiExtensionAddonEnabled", addonOptions.WiiExtensionAddonEnabled); addUsedPinsArray(doc); diff --git a/src/gp2040.cpp b/src/gp2040.cpp index b38675c2a..2e1b8ce3f 100644 --- a/src/gp2040.cpp +++ b/src/gp2040.cpp @@ -17,6 +17,7 @@ #include "addons/reverse.h" #include "addons/turbo.h" #include "addons/slider_socd.h" +#include "addons/wiiext.h" // Pico includes #include "pico/bootrom.h" @@ -104,6 +105,7 @@ void GP2040::setup() { addons.LoadAddon(new JSliderInput(), CORE0_INPUT); addons.LoadAddon(new ReverseInput(), CORE0_INPUT); addons.LoadAddon(new TurboInput(), CORE0_INPUT); + addons.LoadAddon(new WiiExtensionInput(), CORE0_INPUT); addons.LoadAddon(new PlayerNumAddon(), CORE0_USBREPORT); addons.LoadAddon(new SliderSOCDInput(), CORE0_INPUT); } diff --git a/src/storagemanager.cpp b/src/storagemanager.cpp index 1f0aca91a..780939e87 100644 --- a/src/storagemanager.cpp +++ b/src/storagemanager.cpp @@ -30,6 +30,7 @@ #include "addons/reverse.h" #include "addons/turbo.h" #include "addons/slider_socd.h" +#include "addons/wiiext.h" #include "bitmaps.h" @@ -195,6 +196,10 @@ void Storage::setDefaultAddonOptions() addonOptions.sliderSOCDModeOne = SLIDER_SOCD_SLOT_ONE; addonOptions.sliderSOCDModeTwo = SLIDER_SOCD_SLOT_TWO; addonOptions.sliderSOCDModeDefault = SLIDER_SOCD_SLOT_DEFAULT; + addonOptions.wiiExtensionSDAPin = WII_EXTENSION_I2C_SDA_PIN; + addonOptions.wiiExtensionSCLPin = WII_EXTENSION_I2C_SCL_PIN; + addonOptions.wiiExtensionBlock = (WII_EXTENSION_I2C_BLOCK == i2c0) ? 0 : 1; + addonOptions.wiiExtensionSpeed = WII_EXTENSION_I2C_SPEED; addonOptions.AnalogInputEnabled = ANALOG_INPUT_ENABLED; addonOptions.BoardLedAddonEnabled = BOARD_LED_ENABLED; addonOptions.BootselButtonAddonEnabled = BOOTSEL_BUTTON_ENABLED; @@ -208,6 +213,7 @@ void Storage::setDefaultAddonOptions() addonOptions.PS4ModeAddonEnabled = PS4MODE_ADDON_ENABLED; addonOptions.ReverseInputEnabled = REVERSE_ENABLED; addonOptions.TurboInputEnabled = TURBO_ENABLED; + addonOptions.WiiExtensionAddonEnabled = WII_EXTENSION_ENABLED; setAddonOptions(addonOptions); }