From d7ee1bf1ac1750589758eac6ec5553e7fd352939 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 29 May 2020 14:04:27 -0400 Subject: [PATCH 01/19] Change I2C request variable from data to dataOut Avoiding name confusion with the class member struct "data" --- src/internal/ExtensionController.cpp | 4 ++-- src/internal/ExtensionController.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/internal/ExtensionController.cpp b/src/internal/ExtensionController.cpp index a2d18a5..190d563 100644 --- a/src/internal/ExtensionController.cpp +++ b/src/internal/ExtensionController.cpp @@ -157,8 +157,8 @@ boolean ExtensionController::writeRegister(NXC_I2C_TYPE& i2c, byte reg, byte val return i2c_writeRegister(i2c, I2C_Addr, reg, value); } -boolean ExtensionController::requestData(NXC_I2C_TYPE& i2c, uint8_t ptr, size_t size, uint8_t* data) { - return i2c_readDataArray(i2c, I2C_Addr, ptr, size, data); +boolean ExtensionController::requestData(NXC_I2C_TYPE& i2c, uint8_t ptr, size_t size, uint8_t* dataOut) { + return i2c_readDataArray(i2c, I2C_Addr, ptr, size, dataOut); } boolean ExtensionController::requestControlData(NXC_I2C_TYPE& i2c, size_t size, uint8_t* controlData) { diff --git a/src/internal/ExtensionController.h b/src/internal/ExtensionController.h index 462a0dc..7e9dca4 100644 --- a/src/internal/ExtensionController.h +++ b/src/internal/ExtensionController.h @@ -85,7 +85,7 @@ class ExtensionController { static boolean writeRegister(NXC_I2C_TYPE& i2c, byte reg, byte value); - static boolean requestData(NXC_I2C_TYPE& i2c, uint8_t ptr, size_t size, uint8_t* data); + static boolean requestData(NXC_I2C_TYPE& i2c, uint8_t ptr, size_t size, uint8_t* dataOut); static boolean requestControlData(NXC_I2C_TYPE& i2c, size_t size, uint8_t* controlData); static boolean requestIdentity(NXC_I2C_TYPE& i2c, uint8_t* idData); From 7c08e4c5ffecc8729e29ea5115d0a55dbc49fa3b Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 29 May 2020 14:10:40 -0400 Subject: [PATCH 02/19] Add readRegister function --- keywords.txt | 1 + src/internal/ExtensionController.cpp | 4 ++++ src/internal/ExtensionController.h | 2 ++ src/internal/NXC_Comms.h | 4 ++++ 4 files changed, 11 insertions(+) diff --git a/keywords.txt b/keywords.txt index 4948a6e..ab3c0de 100644 --- a/keywords.txt +++ b/keywords.txt @@ -65,6 +65,7 @@ printDebugRaw KEYWORD2 initialize KEYWORD2 writeRegister KEYWORD2 +readRegister KEYWORD2 requestData KEYWORD2 requestControlData KEYWORD2 diff --git a/src/internal/ExtensionController.cpp b/src/internal/ExtensionController.cpp index 190d563..ccb76c4 100644 --- a/src/internal/ExtensionController.cpp +++ b/src/internal/ExtensionController.cpp @@ -157,6 +157,10 @@ boolean ExtensionController::writeRegister(NXC_I2C_TYPE& i2c, byte reg, byte val return i2c_writeRegister(i2c, I2C_Addr, reg, value); } +boolean ExtensionController::readRegister(NXC_I2C_TYPE& i2c, byte reg, uint8_t* dataOut) { + return i2c_readRegister(i2c, I2C_Addr, reg, dataOut); +} + boolean ExtensionController::requestData(NXC_I2C_TYPE& i2c, uint8_t ptr, size_t size, uint8_t* dataOut) { return i2c_readDataArray(i2c, I2C_Addr, ptr, size, dataOut); } diff --git a/src/internal/ExtensionController.h b/src/internal/ExtensionController.h index 7e9dca4..c73a30e 100644 --- a/src/internal/ExtensionController.h +++ b/src/internal/ExtensionController.h @@ -84,6 +84,7 @@ class ExtensionController { static boolean initialize(NXC_I2C_TYPE& i2c); static boolean writeRegister(NXC_I2C_TYPE& i2c, byte reg, byte value); + static boolean readRegister(NXC_I2C_TYPE& i2c, byte reg, uint8_t* dataOut); static boolean requestData(NXC_I2C_TYPE& i2c, uint8_t ptr, size_t size, uint8_t* dataOut); static boolean requestControlData(NXC_I2C_TYPE& i2c, size_t size, uint8_t* controlData); @@ -95,6 +96,7 @@ class ExtensionController { inline boolean initialize() const { return initialize(data.i2c); } inline boolean writeRegister(byte reg, byte value) const { return writeRegister(data.i2c, reg, value); } + inline boolean readRegister(byte reg, uint8_t* dataOut) const { return readRegister(data.i2c, reg, dataOut); } inline boolean requestData(uint8_t ptr, size_t size, uint8_t* dataOut) const { return requestData(data.i2c, ptr, size, dataOut); } inline boolean requestControlData(size_t size, uint8_t* controlData) const { return requestControlData(data.i2c, size, controlData); } diff --git a/src/internal/NXC_Comms.h b/src/internal/NXC_Comms.h index b5e8b94..5b0c3f3 100644 --- a/src/internal/NXC_Comms.h +++ b/src/internal/NXC_Comms.h @@ -71,6 +71,10 @@ namespace NintendoExtensionCtrl { if (!i2c_writePointer(i2c, addr, ptr)) return false; // Set start for data read return i2c_requestMultiple(i2c, addr, requestSize, dataOut); } + + inline boolean i2c_readRegister(NXC_I2C_TYPE& i2c, byte addr, byte reg, uint8_t* dataOut) { + return i2c_readDataArray(i2c, addr, reg, 1, dataOut); // read one register + } } #endif From 9a092c4ebc76289e94417b8d094797df6148cb72 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 29 May 2020 14:33:58 -0400 Subject: [PATCH 03/19] Add readRegister version without error report This is more how you expect the function to work, although it doesn't have any error reporting so you get what you get. --- src/internal/ExtensionController.cpp | 6 ++++++ src/internal/ExtensionController.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/internal/ExtensionController.cpp b/src/internal/ExtensionController.cpp index ccb76c4..8dd75b8 100644 --- a/src/internal/ExtensionController.cpp +++ b/src/internal/ExtensionController.cpp @@ -161,6 +161,12 @@ boolean ExtensionController::readRegister(NXC_I2C_TYPE& i2c, byte reg, uint8_t* return i2c_readRegister(i2c, I2C_Addr, reg, dataOut); } +uint8_t ExtensionController::readRegister(NXC_I2C_TYPE& i2c, byte reg) { + uint8_t regOut = 0x00; + i2c_readRegister(i2c, I2C_Addr, reg, ®Out); + return regOut; // return the value read whether it's valid or not +} + boolean ExtensionController::requestData(NXC_I2C_TYPE& i2c, uint8_t ptr, size_t size, uint8_t* dataOut) { return i2c_readDataArray(i2c, I2C_Addr, ptr, size, dataOut); } diff --git a/src/internal/ExtensionController.h b/src/internal/ExtensionController.h index c73a30e..f9372f2 100644 --- a/src/internal/ExtensionController.h +++ b/src/internal/ExtensionController.h @@ -85,6 +85,7 @@ class ExtensionController { static boolean writeRegister(NXC_I2C_TYPE& i2c, byte reg, byte value); static boolean readRegister(NXC_I2C_TYPE& i2c, byte reg, uint8_t* dataOut); + static uint8_t readRegister(NXC_I2C_TYPE& i2c, byte reg); // no error reporting static boolean requestData(NXC_I2C_TYPE& i2c, uint8_t ptr, size_t size, uint8_t* dataOut); static boolean requestControlData(NXC_I2C_TYPE& i2c, size_t size, uint8_t* controlData); @@ -97,6 +98,7 @@ class ExtensionController { inline boolean writeRegister(byte reg, byte value) const { return writeRegister(data.i2c, reg, value); } inline boolean readRegister(byte reg, uint8_t* dataOut) const { return readRegister(data.i2c, reg, dataOut); } + inline uint8_t readRegister(byte reg) const { return readRegister(data.i2c, reg); } inline boolean requestData(uint8_t ptr, size_t size, uint8_t* dataOut) const { return requestData(data.i2c, ptr, size, dataOut); } inline boolean requestControlData(size_t size, uint8_t* controlData) const { return requestControlData(data.i2c, size, controlData); } From dd42635fe3b6c716899a6210fd15b01c9cab9ff2 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 29 May 2020 15:01:23 -0400 Subject: [PATCH 04/19] Make I2C member functions public This allows users to write arbitrary data to the controller outside of the controller-specific class. Using this functions in the wrong place has the possibility to "break" the controller data flow. User beware... The advantage of course is that users can write and read arbitrary data without having to build a custom class for minor tweaks. --- src/internal/ExtensionController.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/internal/ExtensionController.h b/src/internal/ExtensionController.h index f9372f2..b0a885c 100644 --- a/src/internal/ExtensionController.h +++ b/src/internal/ExtensionController.h @@ -75,12 +75,13 @@ class ExtensionController { static const uint8_t MinRequestSize = 6; // Smallest reporting mode (0x37) static const uint8_t MaxRequestSize = ExtensionData::ControlDataSize; -public: NXC_I2C_TYPE& i2c() const; // Easily accessible I2C reference static const uint8_t I2C_Addr = 0x52; // Address for all extension controllers static const uint8_t ID_Size = 6; // Number of bytes for ID signature +public: + /* I2C Communication Functions, Static & Shared */ static boolean initialize(NXC_I2C_TYPE& i2c); static boolean writeRegister(NXC_I2C_TYPE& i2c, byte reg, byte value); @@ -93,7 +94,7 @@ class ExtensionController { static ExtensionType identifyController(NXC_I2C_TYPE& i2c); -protected: + /* I2C Communication Functions, Inline Member */ inline boolean initialize() const { return initialize(data.i2c); } inline boolean writeRegister(byte reg, byte value) const { return writeRegister(data.i2c, reg, value); } From b6af9540255a12304762939ccc75efc7fd78c7e6 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 29 May 2020 15:36:37 -0400 Subject: [PATCH 05/19] Verify register setting for highRes mode Only return a 'success' condition if the register setting is retained by the controller. Attempting to weed out the controllers that don't support the set mode. --- src/controllers/ClassicController.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index b72ae16..cd95bc4 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -101,14 +101,24 @@ constexpr BitMap ClassicController_Shared::MapsHR::ButtonHome; boolean ClassicController_Shared::setHighRes(boolean hr) { - const uint8_t regVal = hr ? 0x03 : 0x01; // 0x03 for high res, 0x01 for standard - boolean success = writeRegister(0xFE, regVal); // write to controller - if (success == true) { - highRes = hr; // save 'high res' setting + const uint8_t Register = 0xFE; // address of the register we're altering + const uint8_t Setting_Std = 0x01; // the register's value for 'standard' mode + const uint8_t Setting_HR = 0x03; // the register's value for 'high res' mode + + const uint8_t RegVal = hr ? Setting_HR : Setting_Std; + + if (writeRegister(Register, RegVal)) { // write to controller + uint8_t r = readRegister(Register); // read back the saved value + + if (r == RegVal) highRes = hr; // success! saved value matches the sent one + else highRes = !hr; // fail, sent value was not retained. Must be the other one. + if (highRes == true && getRequestSize() < 8) setRequestSize(8); // 8 bytes needed for hr mode else if (highRes == false) setRequestSize(MinRequestSize); // if not in HR, set back to min } - return success; + + // success if the value we're using is the one we tried to set + return hr == getHighRes(); } boolean ClassicController_Shared::getHighRes() const { From 4fad6e8b67e63e771d1ea81b27b59d6aadf363b0 Mon Sep 17 00:00:00 2001 From: David Madison Date: Tue, 2 Jun 2020 23:04:48 -0400 Subject: [PATCH 06/19] Revert "Verify register setting for highRes mode" Turns out this doesn't work. Some controllers change the register but don't actually adjust the mode, making this useless. This reverts commit b6af9540255a12304762939ccc75efc7fd78c7e6. --- src/controllers/ClassicController.cpp | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index cd95bc4..b72ae16 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -101,24 +101,14 @@ constexpr BitMap ClassicController_Shared::MapsHR::ButtonHome; boolean ClassicController_Shared::setHighRes(boolean hr) { - const uint8_t Register = 0xFE; // address of the register we're altering - const uint8_t Setting_Std = 0x01; // the register's value for 'standard' mode - const uint8_t Setting_HR = 0x03; // the register's value for 'high res' mode - - const uint8_t RegVal = hr ? Setting_HR : Setting_Std; - - if (writeRegister(Register, RegVal)) { // write to controller - uint8_t r = readRegister(Register); // read back the saved value - - if (r == RegVal) highRes = hr; // success! saved value matches the sent one - else highRes = !hr; // fail, sent value was not retained. Must be the other one. - + const uint8_t regVal = hr ? 0x03 : 0x01; // 0x03 for high res, 0x01 for standard + boolean success = writeRegister(0xFE, regVal); // write to controller + if (success == true) { + highRes = hr; // save 'high res' setting if (highRes == true && getRequestSize() < 8) setRequestSize(8); // 8 bytes needed for hr mode else if (highRes == false) setRequestSize(MinRequestSize); // if not in HR, set back to min } - - // success if the value we're using is the one we tried to set - return hr == getHighRes(); + return success; } boolean ClassicController_Shared::getHighRes() const { From 2f105ff79cba379484794ec667f248dc4fe5e21b Mon Sep 17 00:00:00 2001 From: David Madison Date: Tue, 2 Jun 2020 23:30:10 -0400 Subject: [PATCH 07/19] Delete "high res" mode from examples This is going to be handled automatically by the library, defaulting to "high res" mode if it's available. Keeping this function public so it can be overridden if the user *really* wants to change it. --- .../Classic_DebugPrint/Classic_DebugPrint.ino | 2 - .../Classic_Demo/Classic_Demo.ino | 2 - .../Classic_HighRes/Classic_HighRes.ino | 64 ------------------- 3 files changed, 68 deletions(-) delete mode 100644 examples/Classic Controller/Classic_HighRes/Classic_HighRes.ino diff --git a/examples/Classic Controller/Classic_DebugPrint/Classic_DebugPrint.ino b/examples/Classic Controller/Classic_DebugPrint/Classic_DebugPrint.ino index d1739d2..4ae96bd 100644 --- a/examples/Classic Controller/Classic_DebugPrint/Classic_DebugPrint.ino +++ b/examples/Classic Controller/Classic_DebugPrint/Classic_DebugPrint.ino @@ -37,8 +37,6 @@ void setup() { Serial.println("Classic Controller not detected!"); delay(1000); } - - //classic.setHighRes(true); // uncomment to run in high resolution mode } void loop() { diff --git a/examples/Classic Controller/Classic_Demo/Classic_Demo.ino b/examples/Classic Controller/Classic_Demo/Classic_Demo.ino index 07d692b..2a66832 100644 --- a/examples/Classic Controller/Classic_Demo/Classic_Demo.ino +++ b/examples/Classic Controller/Classic_Demo/Classic_Demo.ino @@ -36,8 +36,6 @@ void setup() { Serial.println("Classic Controller not detected!"); delay(1000); } - - //classic.setHighRes(true); // uncomment to run in high resolution mode } void loop() { diff --git a/examples/Classic Controller/Classic_HighRes/Classic_HighRes.ino b/examples/Classic Controller/Classic_HighRes/Classic_HighRes.ino deleted file mode 100644 index 333b3a3..0000000 --- a/examples/Classic Controller/Classic_HighRes/Classic_HighRes.ino +++ /dev/null @@ -1,64 +0,0 @@ -/* -* Project Nintendo Extension Controller Library -* @author David Madison -* @link github.com/dmadison/NintendoExtensionCtrl -* @license LGPLv3 - Copyright (c) 2020 David Madison -* -* This file is part of the Nintendo Extension Controller Library. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -* -* Example: Classic_HighRes -* Description: Connect to a Classic Controller and demonstrate the "high res" -* mode by switching back and forth between the standard and -* high resolution maps. -*/ - -#include - -const unsigned long SwitchTime = 6000; // ms, time to switch between low and high res modes (6 seconds) -unsigned long lastSwitch = 0; // timestamp, when the last switch was made - -ClassicController classic; - -void setup() { - Serial.begin(115200); - classic.begin(); - - while (!classic.connect()) { - Serial.println("Classic Controller not detected!"); - delay(1000); - } - classic.setHighRes(true); // let's start in high res mode -} - -void loop() { - boolean success = classic.update(); // Get new data from the controller - - if (success == true) { // We've got data! - classic.printDebug(); // Print all of the values! - - if (millis() - lastSwitch >= SwitchTime) { // wait until it's time to switch - lastSwitch = millis(); // save the timestamp - boolean hr = classic.getHighRes(); // are we in high res or not? - hr = !hr; // flip it, let's go to the other one - classic.setHighRes(hr); // set the new mode - } - } - else { // Data is bad :( - Serial.println("Controller Disconnected!"); - delay(1000); - classic.connect(); - } -} From be49de5cbfe40b57d860297c0749be3a79e76e76 Mon Sep 17 00:00:00 2001 From: David Madison Date: Tue, 2 Jun 2020 23:44:30 -0400 Subject: [PATCH 08/19] Shift ClassicController data into high res range This way the data output ranges match regardless of whether the controller is in "standard" or "high resolution" mode. --- .../Classic_Demo/Classic_Demo.ino | 8 ++----- src/controllers/ClassicController.cpp | 23 ++++++++++--------- src/controllers/ClassicController.h | 6 ++--- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/examples/Classic Controller/Classic_Demo/Classic_Demo.ino b/examples/Classic Controller/Classic_Demo/Classic_Demo.ino index 2a66832..96ccaa1 100644 --- a/examples/Classic Controller/Classic_Demo/Classic_Demo.ino +++ b/examples/Classic Controller/Classic_Demo/Classic_Demo.ino @@ -70,17 +70,13 @@ void loop() { Serial.println("released"); } - // Read a joystick axis (left XY, right XY) - // Standard Mode: 0-63 Left, 0-31 Right - // High Resolution Mode: 0-255 Left/Right + // Read a joystick axis (0-255, left XY, right XY) int joyLX = classic.leftJoyX(); Serial.print("The left joystick's X axis is at "); Serial.println(joyLX); - // Read a trigger (L/R) - // Standard Mode: 0-31 - // High Resolution Mode: 0-255 + // Read a trigger (0-255, L/R) int triggerL = classic.triggerL(); Serial.print("The left trigger is at "); diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index b72ae16..2c69e05 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -91,12 +91,13 @@ constexpr BitMap ClassicController_Shared::MapsHR::ButtonHome; * to copy the mapping name twice. * * If the controller is not in "high resolution" mode (according to the class - * data), return the controlData or controlBit function for the specified map - * in the "standard" maps (Maps::). If the controller *is* in "high resolution" - * mode, return the controlData or controlBit function for the specified map - * of the same name in the "high resolution" maps (MapsHR::). + * data), fetch the controlData or controlBit function for the specified map + * in the "standard" maps (Maps::) and bit shift to the left in order to fit + * the full width of the "high res" data range. If the controller *is* in + * "high resolution" mode, fetch the controlData or controlBit function for + * the specified mapof the same name in the "high resolution" maps (MapsHR::). */ -#define HRDATA(map) !highRes ? getControlData(Maps::map) : getControlData(MapsHR::map) +#define HRDATA(map, shift) !highRes ? (getControlData(Maps::map) << shift) & 0xFF : getControlData(MapsHR::map) #define HRBIT(map) !highRes ? getControlBit(Maps::map) : getControlBit(MapsHR::map) @@ -116,19 +117,19 @@ boolean ClassicController_Shared::getHighRes() const { } uint8_t ClassicController_Shared::leftJoyX() const { - return HRDATA(LeftJoyX); + return HRDATA(LeftJoyX, 2); // 6 bits for standard range, so shift left (8-6) } uint8_t ClassicController_Shared::leftJoyY() const { - return HRDATA(LeftJoyY); + return HRDATA(LeftJoyY, 2); } uint8_t ClassicController_Shared::rightJoyX() const { - return HRDATA(RightJoyX); + return HRDATA(RightJoyX, 3); // 5 bits for standard range, so shift left (8-5) } uint8_t ClassicController_Shared::rightJoyY() const { - return HRDATA(RightJoyY); + return HRDATA(RightJoyY, 3); } boolean ClassicController_Shared::dpadUp() const { @@ -164,11 +165,11 @@ boolean ClassicController_Shared::buttonY() const { } uint8_t ClassicController_Shared::triggerL() const { - return HRDATA(TriggerL); + return HRDATA(TriggerL, 3); // 5 bits for standard range, so shift left (8-5) } uint8_t ClassicController_Shared::triggerR() const { - return HRDATA(TriggerR); + return HRDATA(TriggerR, 3); } boolean ClassicController_Shared::buttonL() const { diff --git a/src/controllers/ClassicController.h b/src/controllers/ClassicController.h index 80ec800..fbab016 100644 --- a/src/controllers/ClassicController.h +++ b/src/controllers/ClassicController.h @@ -117,10 +117,10 @@ namespace NintendoExtensionCtrl { boolean setHighRes(boolean hr = true); boolean getHighRes() const; - uint8_t leftJoyX() const; // 6 bits, 0-63 + uint8_t leftJoyX() const; // 8 bits, 6 shifted in std mode uint8_t leftJoyY() const; - uint8_t rightJoyX() const; // 5 bits, 0-31 + uint8_t rightJoyX() const; // 8 bits, 5 shifted in std mode uint8_t rightJoyY() const; boolean dpadUp() const; @@ -133,7 +133,7 @@ namespace NintendoExtensionCtrl { boolean buttonX() const; boolean buttonY() const; - uint8_t triggerL() const; // 5 bits, 0-31 + uint8_t triggerL() const; // 8 bits, 5 shifted in std mode uint8_t triggerR() const; boolean buttonL() const; From 91d870ec9f895add0fe70bda4f8769a7b212464d Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 04:07:03 -0400 Subject: [PATCH 09/19] Added function to check Classic "high res" mode I'm weirdly proud of how elegant this extremely cursed solution is. --- src/controllers/ClassicController.cpp | 82 +++++++++++++++++++++++++-- src/controllers/ClassicController.h | 1 + 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index 2c69e05..ff3a76f 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -101,15 +101,85 @@ constexpr BitMap ClassicController_Shared::MapsHR::ButtonHome; #define HRBIT(map) !highRes ? getControlBit(Maps::map) : getControlBit(MapsHR::map) +boolean ClassicController_Shared::checkHighRes() const { + /* Programmator Emptor: vvv This is where all of the headaches stem from vvv */ + + /* Okay, so here's the deal. The Wii Classic Controller reports its data + * as six bytes with bit-packing for the analog values. When the NES and + * SNES mini consoles were released it turned out that Nintendo had + * included a "high resolution" mode for the Classic Controller. Writing + * '0x03' to the register '0xFE' will make the controller output 8 bytes, + * with each analog control surface using a full byte for its output. + * + * So here's the rub: + * * Bad knockoff Classic Controllers only support "normal" mode + * * Bad knockoff NES Controllers only support "high resolution" mode + * * Genuine controllers support both + * + * Some knockoffs will behave properly and switch between the modes as + * requested, but many will only report their data in one mode and ignore + * the host if it asks otherwise. This results in control data that is + * misinterpreted and users that are unhappy. So not only do we have to + * switch between modes, but we need to come up with a robust method + * to figure out *what mode we're in*. + * + * Here's my idea: in "standard" mode, the controller outputs 6 bytes of + * control data, leaving bytes 7-8 blank (0x00). If we read these two bytes + * and they have data in them, the controller must be in high resolution + * mode! In theory, at least. + * + * This is complicated by the fact that the data from the I2C bus has no + * error checking and is open drain, so if the pull-ups are too weak or + * there is noise on the bus some of these bits may flip 'high' and then + * the check is no good. + * + * To mitigate this, the same data set is requested twice and compared + * against itself. If there is a data mismatch, the requests are repeated + * until the two arrays agree. Not perfect, but better than nothing. + * + * Note that this read starts at 0x00. I tried starting at where the data + * *actually starts* (bytes 7 and 8, i.e. ptr 0x06), but the knockoff + * controllers apparently don't understand how to act as a proper + * register-based I2C device and just return junk. So instead we're starting + * at the beginning of the data block. + * + * On the plus side, requesting more data here does make the error-checking + * more robust! Although at the expensive of a longer delay. + */ + static const uint8_t CheckPtr = 0x00; // start of the control data block + static const uint8_t CheckSize = 8; // 8 bytes to cover both std and high res + uint8_t checkData[CheckSize] = { 0x00 }, verifyData[CheckSize] = { 0x00 }; + do { + if (!requestData(CheckPtr, CheckSize, checkData)) return false; + delayMicroseconds(I2C_ConversionDelay); // need a brief delay between reads + if (!requestData(CheckPtr, CheckSize, verifyData)) return false; + + boolean equal = true; + for (uint8_t i = 0; i < CheckSize; i++) { + equal &= (checkData[i] == verifyData[i]); + } + + if (equal) break; // if data matches, continue + delayMicroseconds(I2C_ConversionDelay); // if we're doing another loop, wait between reads again + } while (true); + + return !(checkData[6] == 0x00 && checkData[7] == 0x00); // if both are '0', high res is false +} + boolean ClassicController_Shared::setHighRes(boolean hr) { const uint8_t regVal = hr ? 0x03 : 0x01; // 0x03 for high res, 0x01 for standard - boolean success = writeRegister(0xFE, regVal); // write to controller - if (success == true) { - highRes = hr; // save 'high res' setting - if (highRes == true && getRequestSize() < 8) setRequestSize(8); // 8 bytes needed for hr mode - else if (highRes == false) setRequestSize(MinRequestSize); // if not in HR, set back to min + if (!writeRegister(0xFE, regVal)) return false; // write to controller + + highRes = checkHighRes(); // check controller's HR setting and save to class + + if (getHighRes() == true && getRequestSize() < 8) { + setRequestSize(8); // 8 bytes needed for hr mode + } + else if (getHighRes() == false && hr == false) { + setRequestSize(MinRequestSize); // if not in HR and *trying* not to be, set back to min } - return success; + + return hr == highRes; // 'success' if the value we're setting is the one we read } boolean ClassicController_Shared::getHighRes() const { diff --git a/src/controllers/ClassicController.h b/src/controllers/ClassicController.h index fbab016..5587ef3 100644 --- a/src/controllers/ClassicController.h +++ b/src/controllers/ClassicController.h @@ -154,6 +154,7 @@ namespace NintendoExtensionCtrl { protected: boolean highRes = false; // 'high resolution' mode setting + boolean checkHighRes() const; }; From 62dae4cd140981b8b3ddbfc5f5542a196b53b030 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 04:50:13 -0400 Subject: [PATCH 10/19] Set ClassicController high res mode on connect This should fix the class so it supports Classic knockoffs, NES knockoffs, and genuine controllers without any extra input from the user. --- src/controllers/ClassicController.cpp | 16 ++++++++++++++++ src/controllers/ClassicController.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index ff3a76f..9aec7de 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -101,6 +101,22 @@ constexpr BitMap ClassicController_Shared::MapsHR::ButtonHome; #define HRBIT(map) !highRes ? getControlBit(Maps::map) : getControlBit(MapsHR::map) +boolean ClassicController_Shared::specificInit() { + /* On init, try to set the controller to work in "high resolution" mode so + * we get a full byte of data for each analog input. + * + * The 'setHighRes' function also checks the current mode of the controller + * after the HR setting is set, so the data maps should match the data + * reporting type. This way the class flexes to support controllers that + * only work in standard mode, only work in high res mode, or can support + * both. + */ + delayMicroseconds(I2C_ConversionDelay); // wait after ID read before writing register + setHighRes(true); // attempt to set, otherwise will read mode from controller + + return true; // unconditional connection +} + boolean ClassicController_Shared::checkHighRes() const { /* Programmator Emptor: vvv This is where all of the headaches stem from vvv */ diff --git a/src/controllers/ClassicController.h b/src/controllers/ClassicController.h index 5587ef3..12a47a2 100644 --- a/src/controllers/ClassicController.h +++ b/src/controllers/ClassicController.h @@ -114,6 +114,8 @@ namespace NintendoExtensionCtrl { ClassicController_Shared(ExtensionPort &port) : ClassicController_Shared(port.getExtensionData()) {} + boolean specificInit(); + boolean setHighRes(boolean hr = true); boolean getHighRes() const; From ba5d60e5e09152c2c4a5d9984897d5d7680de422 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 05:03:33 -0400 Subject: [PATCH 11/19] Use pointer argument for checkHighRes function The function now returns the success state of the check: true if it successfully read the 'mode' of the controller (std or high res) and 'false' if there was an I2C communication error. The 'mode' state of the controller is instead set to the bool argument passed by pointer. This prevents accidentally setting the internal 'high res' mode to 'false' if there was a communication error, rather than the function reading the 'standard' map. --- src/controllers/ClassicController.cpp | 9 ++++++--- src/controllers/ClassicController.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index 9aec7de..cee2e7e 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -117,7 +117,7 @@ boolean ClassicController_Shared::specificInit() { return true; // unconditional connection } -boolean ClassicController_Shared::checkHighRes() const { +boolean ClassicController_Shared::checkHighRes(boolean *hr) const { /* Programmator Emptor: vvv This is where all of the headaches stem from vvv */ /* Okay, so here's the deal. The Wii Classic Controller reports its data @@ -179,14 +179,17 @@ boolean ClassicController_Shared::checkHighRes() const { delayMicroseconds(I2C_ConversionDelay); // if we're doing another loop, wait between reads again } while (true); - return !(checkData[6] == 0x00 && checkData[7] == 0x00); // if both are '0', high res is false + *hr = !(checkData[6] == 0x00 && checkData[7] == 0x00); // if both are '0', high res is false + return true; // successfully read state } boolean ClassicController_Shared::setHighRes(boolean hr) { const uint8_t regVal = hr ? 0x03 : 0x01; // 0x03 for high res, 0x01 for standard if (!writeRegister(0xFE, regVal)) return false; // write to controller - highRes = checkHighRes(); // check controller's HR setting and save to class + boolean currentMode = false; // check controller's HR setting + if (!checkHighRes(¤tMode)) return false; // error: could not read mode + highRes = currentMode; // save current mode to class if (getHighRes() == true && getRequestSize() < 8) { setRequestSize(8); // 8 bytes needed for hr mode diff --git a/src/controllers/ClassicController.h b/src/controllers/ClassicController.h index 12a47a2..948ec35 100644 --- a/src/controllers/ClassicController.h +++ b/src/controllers/ClassicController.h @@ -156,7 +156,7 @@ namespace NintendoExtensionCtrl { protected: boolean highRes = false; // 'high resolution' mode setting - boolean checkHighRes() const; + boolean checkHighRes(boolean *hr) const; }; From 5a6e9c71fe9ccd4844c48596b8c4758dd6b3f28f Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 05:06:14 -0400 Subject: [PATCH 12/19] Remove MiniControllerBase This is no longer needed since the base class now flexes for all controller types. --- src/controllers/ClassicController.cpp | 7 ------- src/controllers/ClassicController.h | 13 +++---------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index cee2e7e..8d3399a 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -342,13 +342,6 @@ void ClassicController_Shared::printDebug(Print& output) const { // ######### Mini Controller Support ######### -boolean MiniControllerBase::specificInit() { - // all mini controllers use high res format, and some of the cheaper third - // party ones will not work without it. So we're going to set this on - // connection for all of them - return setHighRes(true); -} - void NESMiniController_Shared::printDebug(Print& output) const { const char fillCharacter = '_'; diff --git a/src/controllers/ClassicController.h b/src/controllers/ClassicController.h index 948ec35..f51d606 100644 --- a/src/controllers/ClassicController.h +++ b/src/controllers/ClassicController.h @@ -162,23 +162,16 @@ namespace NintendoExtensionCtrl { /* Nintendo Mini Console Controllers */ - class MiniControllerBase : public ClassicController_Shared { + class NESMiniController_Shared : public ClassicController_Shared { public: using ClassicController_Shared::ClassicController_Shared; - boolean specificInit(); - }; - - class NESMiniController_Shared : public MiniControllerBase { - public: - using MiniControllerBase::MiniControllerBase; - void printDebug(Print& output = NXC_SERIAL_DEFAULT) const; }; - class SNESMiniController_Shared : public MiniControllerBase { + class SNESMiniController_Shared : public ClassicController_Shared { public: - using MiniControllerBase::MiniControllerBase; + using ClassicController_Shared::ClassicController_Shared; void printDebug(Print& output = NXC_SERIAL_DEFAULT) const; }; From 66278089262ebe3ea14d90ec28f4748567d79bb0 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 05:15:03 -0400 Subject: [PATCH 13/19] Simplify Classic Controller debugPrint output Using the pipe separator to match the formatting of the other data. --- src/controllers/ClassicController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index 8d3399a..bcb7605 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -334,7 +334,7 @@ void ClassicController_Shared::printDebug(Print& output) const { zlButtonPrint, zrButtonPrint); output.print(buffer); - if (getHighRes()) output.print(" (High Res)"); + if (getHighRes()) output.print(" | (HR)"); output.println(); } From 3e2c364c8c01b28ec72c5a4e5368ed88d14366f5 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 05:21:39 -0400 Subject: [PATCH 14/19] Improve efficiency of checkHighRes verify loop Marginally, at least. This will short-circuit the data verify loop early if a byte does not match. --- src/controllers/ClassicController.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index bcb7605..7381960 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -172,7 +172,10 @@ boolean ClassicController_Shared::checkHighRes(boolean *hr) const { boolean equal = true; for (uint8_t i = 0; i < CheckSize; i++) { - equal &= (checkData[i] == verifyData[i]); + if (checkData[i] != verifyData[i]) { + equal = false; // one byte does not match! quit + break; + } } if (equal) break; // if data matches, continue From eddd9f783409121f2d69562ec0af69145499de14 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 05:31:36 -0400 Subject: [PATCH 15/19] Only verify relevant bytes in checkHighRes In 'high res' mode the analog sticks can drift a little bit, so this was needlessly repeating reads waiting for a stable input. Instead this only checks the two relevant bytes that need to be stable in order for this check to work. --- src/controllers/ClassicController.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index 7381960..f3699da 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -158,12 +158,10 @@ boolean ClassicController_Shared::checkHighRes(boolean *hr) const { * controllers apparently don't understand how to act as a proper * register-based I2C device and just return junk. So instead we're starting * at the beginning of the data block. - * - * On the plus side, requesting more data here does make the error-checking - * more robust! Although at the expensive of a longer delay. */ - static const uint8_t CheckPtr = 0x00; // start of the control data block - static const uint8_t CheckSize = 8; // 8 bytes to cover both std and high res + static const uint8_t CheckPtr = 0x00; // start of the control data block + static const uint8_t CheckSize = 8; // 8 bytes to cover both std and high res + static const uint8_t DataOffset = 0x06; // start of the data we're interested in (7 / 8) uint8_t checkData[CheckSize] = { 0x00 }, verifyData[CheckSize] = { 0x00 }; do { if (!requestData(CheckPtr, CheckSize, checkData)) return false; @@ -171,7 +169,7 @@ boolean ClassicController_Shared::checkHighRes(boolean *hr) const { if (!requestData(CheckPtr, CheckSize, verifyData)) return false; boolean equal = true; - for (uint8_t i = 0; i < CheckSize; i++) { + for (uint8_t i = 0; i < CheckSize - DataOffset; i++) { if (checkData[i] != verifyData[i]) { equal = false; // one byte does not match! quit break; @@ -182,7 +180,7 @@ boolean ClassicController_Shared::checkHighRes(boolean *hr) const { delayMicroseconds(I2C_ConversionDelay); // if we're doing another loop, wait between reads again } while (true); - *hr = !(checkData[6] == 0x00 && checkData[7] == 0x00); // if both are '0', high res is false + *hr = !(checkData[DataOffset] == 0x00 && checkData[DataOffset+1] == 0x00); // if both are '0', high res is false return true; // successfully read state } From 65b1a228e0201f2c4ecb75962e83a48df9c092d4 Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 16:52:39 -0400 Subject: [PATCH 16/19] Use internal function to read CC high res success This allows 'specificInit' to return false and fail the connection check if there's a communication error, instead of approving the connection and continuing without knowing the controller's current data mode. --- src/controllers/ClassicController.cpp | 15 +++++++++------ src/controllers/ClassicController.h | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index f3699da..3db88f6 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -105,16 +105,14 @@ boolean ClassicController_Shared::specificInit() { /* On init, try to set the controller to work in "high resolution" mode so * we get a full byte of data for each analog input. * - * The 'setHighRes' function also checks the current mode of the controller + * The 'writeHighRes' function also checks the current mode of the controller * after the HR setting is set, so the data maps should match the data * reporting type. This way the class flexes to support controllers that * only work in standard mode, only work in high res mode, or can support * both. */ delayMicroseconds(I2C_ConversionDelay); // wait after ID read before writing register - setHighRes(true); // attempt to set, otherwise will read mode from controller - - return true; // unconditional connection + return writeHighRes(true); // 'success' if no comms errors } boolean ClassicController_Shared::checkHighRes(boolean *hr) const { @@ -184,7 +182,7 @@ boolean ClassicController_Shared::checkHighRes(boolean *hr) const { return true; // successfully read state } -boolean ClassicController_Shared::setHighRes(boolean hr) { +boolean ClassicController_Shared::writeHighRes(boolean hr) { const uint8_t regVal = hr ? 0x03 : 0x01; // 0x03 for high res, 0x01 for standard if (!writeRegister(0xFE, regVal)) return false; // write to controller @@ -199,7 +197,12 @@ boolean ClassicController_Shared::setHighRes(boolean hr) { setRequestSize(MinRequestSize); // if not in HR and *trying* not to be, set back to min } - return hr == highRes; // 'success' if the value we're setting is the one we read + return true; // 'success' if no communication errors, regardless of setting +} + +boolean ClassicController_Shared::setHighRes(boolean hr) { + // 'success' if the mode is changed to the one we're trying to set + return writeHighRes(hr) && (getHighRes() == hr); } boolean ClassicController_Shared::getHighRes() const { diff --git a/src/controllers/ClassicController.h b/src/controllers/ClassicController.h index f51d606..fceed24 100644 --- a/src/controllers/ClassicController.h +++ b/src/controllers/ClassicController.h @@ -157,6 +157,7 @@ namespace NintendoExtensionCtrl { protected: boolean highRes = false; // 'high resolution' mode setting boolean checkHighRes(boolean *hr) const; + boolean writeHighRes(boolean hr); }; From d35e66170390e5ea09440f2143d7d41b58081b1c Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 17:01:32 -0400 Subject: [PATCH 17/19] Refactor internal "highRes" funcs to "dataMode" This isn't explicitly about high resolution mode anymore, but about reading the current data mode of the controller. The public-facing functions will still be kept as "highRes" because it's more idiomatic to check whether a specific mode is 'true' rather than trying to match up the boolean values to a specific mode. --- src/controllers/ClassicController.cpp | 12 ++++++------ src/controllers/ClassicController.h | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index 3db88f6..b107bcc 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -105,17 +105,17 @@ boolean ClassicController_Shared::specificInit() { /* On init, try to set the controller to work in "high resolution" mode so * we get a full byte of data for each analog input. * - * The 'writeHighRes' function also checks the current mode of the controller + * The 'setDataMode' function also checks the current mode of the controller * after the HR setting is set, so the data maps should match the data * reporting type. This way the class flexes to support controllers that * only work in standard mode, only work in high res mode, or can support * both. */ delayMicroseconds(I2C_ConversionDelay); // wait after ID read before writing register - return writeHighRes(true); // 'success' if no comms errors + return setDataMode(true); // try to set 'high res' mode. 'success' if no comms errors } -boolean ClassicController_Shared::checkHighRes(boolean *hr) const { +boolean ClassicController_Shared::checkDataMode(boolean *hr) const { /* Programmator Emptor: vvv This is where all of the headaches stem from vvv */ /* Okay, so here's the deal. The Wii Classic Controller reports its data @@ -182,12 +182,12 @@ boolean ClassicController_Shared::checkHighRes(boolean *hr) const { return true; // successfully read state } -boolean ClassicController_Shared::writeHighRes(boolean hr) { +boolean ClassicController_Shared::setDataMode(boolean hr) { const uint8_t regVal = hr ? 0x03 : 0x01; // 0x03 for high res, 0x01 for standard if (!writeRegister(0xFE, regVal)) return false; // write to controller boolean currentMode = false; // check controller's HR setting - if (!checkHighRes(¤tMode)) return false; // error: could not read mode + if (!checkDataMode(¤tMode)) return false; // error: could not read mode highRes = currentMode; // save current mode to class if (getHighRes() == true && getRequestSize() < 8) { @@ -202,7 +202,7 @@ boolean ClassicController_Shared::writeHighRes(boolean hr) { boolean ClassicController_Shared::setHighRes(boolean hr) { // 'success' if the mode is changed to the one we're trying to set - return writeHighRes(hr) && (getHighRes() == hr); + return setDataMode(hr) && (getHighRes() == hr); } boolean ClassicController_Shared::getHighRes() const { diff --git a/src/controllers/ClassicController.h b/src/controllers/ClassicController.h index fceed24..5de8803 100644 --- a/src/controllers/ClassicController.h +++ b/src/controllers/ClassicController.h @@ -156,8 +156,9 @@ namespace NintendoExtensionCtrl { protected: boolean highRes = false; // 'high resolution' mode setting - boolean checkHighRes(boolean *hr) const; - boolean writeHighRes(boolean hr); + + boolean checkDataMode(boolean *hr) const; + boolean setDataMode(boolean hr); }; From d1a0c63cd02527d7187d77f5c097917b22efe0cf Mon Sep 17 00:00:00 2001 From: David Madison Date: Fri, 5 Jun 2020 17:11:20 -0400 Subject: [PATCH 18/19] Update ClassicCon specificInit block comment Better-describing this function and how it's supposed to work. --- src/controllers/ClassicController.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index b107bcc..6095cca 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -103,13 +103,15 @@ constexpr BitMap ClassicController_Shared::MapsHR::ButtonHome; boolean ClassicController_Shared::specificInit() { /* On init, try to set the controller to work in "high resolution" mode so - * we get a full byte of data for each analog input. + * we get a full byte of data for each analog input. Then read the current + * "data mode" from the controller so that the control surface functions + * use the correct mappings. This way, the class flexes to support + * all controllers regardless of their available data mode. * - * The 'setDataMode' function also checks the current mode of the controller - * after the HR setting is set, so the data maps should match the data - * reporting type. This way the class flexes to support controllers that - * only work in standard mode, only work in high res mode, or can support - * both. + * This function will only return false if there is a *communciation error* + * on the I2C bus, meaning that the controller did not respond to a write + * or did not provide the right amount of data for a request. It will *not* + * return false if the "high resolution" mode is not successfully set. */ delayMicroseconds(I2C_ConversionDelay); // wait after ID read before writing register return setDataMode(true); // try to set 'high res' mode. 'success' if no comms errors From c8f00579ca7d67a2f058d39e25bf7a84c657929f Mon Sep 17 00:00:00 2001 From: David Madison Date: Sun, 7 Jun 2020 05:42:57 -0400 Subject: [PATCH 19/19] Make verification optional for CC setHighRes As requested in #63. Useful for testing or if the user wants to implement their own verification function. --- src/controllers/ClassicController.cpp | 17 +++++++++++------ src/controllers/ClassicController.h | 4 ++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/controllers/ClassicController.cpp b/src/controllers/ClassicController.cpp index 6095cca..84fa099 100644 --- a/src/controllers/ClassicController.cpp +++ b/src/controllers/ClassicController.cpp @@ -184,13 +184,18 @@ boolean ClassicController_Shared::checkDataMode(boolean *hr) const { return true; // successfully read state } -boolean ClassicController_Shared::setDataMode(boolean hr) { +boolean ClassicController_Shared::setDataMode(boolean hr, boolean verify) { const uint8_t regVal = hr ? 0x03 : 0x01; // 0x03 for high res, 0x01 for standard if (!writeRegister(0xFE, regVal)) return false; // write to controller - boolean currentMode = false; // check controller's HR setting - if (!checkDataMode(¤tMode)) return false; // error: could not read mode - highRes = currentMode; // save current mode to class + if (verify == true) { + boolean currentMode = false; // check controller's HR setting + if (!checkDataMode(¤tMode)) return false; // error: could not read mode + highRes = currentMode; // save current mode to class + } + else { + highRes = hr; // save mode (no verification) + } if (getHighRes() == true && getRequestSize() < 8) { setRequestSize(8); // 8 bytes needed for hr mode @@ -202,9 +207,9 @@ boolean ClassicController_Shared::setDataMode(boolean hr) { return true; // 'success' if no communication errors, regardless of setting } -boolean ClassicController_Shared::setHighRes(boolean hr) { +boolean ClassicController_Shared::setHighRes(boolean hr, boolean verify) { // 'success' if the mode is changed to the one we're trying to set - return setDataMode(hr) && (getHighRes() == hr); + return setDataMode(hr, verify) && (getHighRes() == hr); } boolean ClassicController_Shared::getHighRes() const { diff --git a/src/controllers/ClassicController.h b/src/controllers/ClassicController.h index 5de8803..aee35ee 100644 --- a/src/controllers/ClassicController.h +++ b/src/controllers/ClassicController.h @@ -116,7 +116,7 @@ namespace NintendoExtensionCtrl { boolean specificInit(); - boolean setHighRes(boolean hr = true); + boolean setHighRes(boolean hr = true, boolean verify = true); boolean getHighRes() const; uint8_t leftJoyX() const; // 8 bits, 6 shifted in std mode @@ -158,7 +158,7 @@ namespace NintendoExtensionCtrl { boolean highRes = false; // 'high resolution' mode setting boolean checkDataMode(boolean *hr) const; - boolean setDataMode(boolean hr); + boolean setDataMode(boolean hr, boolean verify = true); };