From c0d748fc73fceadbaf91b5befdf0bb0d8a763a08 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Wed, 17 Jul 2019 21:18:21 -0700 Subject: [PATCH] Implement input for balance board --- Source/Core/Core/HW/Wiimote.cpp | 6 + Source/Core/Core/HW/Wiimote.h | 2 + .../HW/WiimoteEmu/Extension/BalanceBoard.cpp | 160 +++++++++++++++--- .../HW/WiimoteEmu/Extension/BalanceBoard.h | 21 +++ Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 7 + Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 2 + .../Config/Mapping/WiimoteEmuExtension.cpp | 21 +++ .../Config/Mapping/WiimoteEmuExtension.h | 2 + 8 files changed, 195 insertions(+), 26 deletions(-) diff --git a/Source/Core/Core/HW/Wiimote.cpp b/Source/Core/Core/HW/Wiimote.cpp index 4b9e95e34d..26a448010d 100644 --- a/Source/Core/Core/HW/Wiimote.cpp +++ b/Source/Core/Core/HW/Wiimote.cpp @@ -160,6 +160,12 @@ ControllerEmu::ControlGroup* GetShinkansenGroup(int number, WiimoteEmu::Shinkans ->GetShinkansenGroup(group); } +ControllerEmu::ControlGroup* GetBalanceBoardGroup(int number, WiimoteEmu::BalanceBoardGroup group) +{ + return static_cast(s_config.GetController(number)) + ->GetBalanceBoardGroup(group); +} + void Shutdown() { s_config.UnregisterHotplugCallback(); diff --git a/Source/Core/Core/HW/Wiimote.h b/Source/Core/Core/HW/Wiimote.h index 1acdc3a184..d0bdc21973 100644 --- a/Source/Core/Core/HW/Wiimote.h +++ b/Source/Core/Core/HW/Wiimote.h @@ -29,6 +29,7 @@ enum class UDrawTabletGroup; enum class DrawsomeTabletGroup; enum class TaTaConGroup; enum class ShinkansenGroup; +enum class BalanceBoardGroup; } // namespace WiimoteEmu enum @@ -94,6 +95,7 @@ ControllerEmu::ControlGroup* GetDrawsomeTabletGroup(int number, WiimoteEmu::DrawsomeTabletGroup group); ControllerEmu::ControlGroup* GetTaTaConGroup(int number, WiimoteEmu::TaTaConGroup group); ControllerEmu::ControlGroup* GetShinkansenGroup(int number, WiimoteEmu::ShinkansenGroup group); +ControllerEmu::ControlGroup* GetBalanceBoardGroup(int number, WiimoteEmu::BalanceBoardGroup group); } // namespace Wiimote namespace WiimoteReal diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/BalanceBoard.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/BalanceBoard.cpp index 38a782f785..eed194fff2 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/BalanceBoard.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/BalanceBoard.cpp @@ -20,10 +20,8 @@ #include "InputCommon/ControllerEmu/Control/Input.h" #include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h" -#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h" #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" -#include "InputCommon/ControllerEmu/ControlGroup/Force.h" -#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h" +#include "InputCommon/ControllerEmu/ControlGroup/Triggers.h" namespace WiimoteEmu { @@ -31,18 +29,36 @@ constexpr std::array balance_board_id{{0x00, 0x00, 0xa4, 0x20, 0x04, 0x02 BalanceBoardExt::BalanceBoardExt() : Extension1stParty("BalanceBoard", _trans("Balance Board")) { + // balance + groups.emplace_back( + m_balance = new ControllerEmu::AnalogStick( + _trans("Balance"), std::make_unique(1.0))); + groups.emplace_back(m_weight = new ControllerEmu::Triggers(_trans("Weight"))); + m_weight->controls.emplace_back( + new ControllerEmu::Input(ControllerEmu::Translatability::Translate, _trans("Weight"))); } void BalanceBoardExt::BuildDesiredExtensionState(DesiredExtensionState* target_state) { + const ControllerEmu::AnalogStick::StateData balance_state = m_balance->GetState(); + const ControllerEmu::Triggers::StateData weight_state = m_weight->GetState(); + const auto weight = weight_state.data[0]; + + const double total_weight = DEFAULT_WEIGHT * weight; // kilograms + + const auto top_right = total_weight * (1 + balance_state.x + balance_state.y) / 4; + const auto bottom_right = total_weight * (1 + balance_state.x - balance_state.y) / 4; + const auto top_left = total_weight * (1 - balance_state.x + balance_state.y) / 4; + const auto bottom_left = total_weight * (1 - balance_state.x - balance_state.y) / 4; + DataFormat bb_data = {}; - bb_data.top_right = Common::swap16(0x0743); - bb_data.bottom_right = Common::swap16(0x11a2); - bb_data.top_left = Common::swap16(0x06a8); - bb_data.bottom_left = Common::swap16(0x4694); - bb_data.temperature = 0x19; - bb_data.battery = 0x83; + bb_data.top_right = Common::swap16(ConvertToSensorWeight(top_right)); + bb_data.bottom_right = Common::swap16(ConvertToSensorWeight(bottom_right)); + bb_data.top_left = Common::swap16(ConvertToSensorWeight(top_left)); + bb_data.bottom_left = Common::swap16(ConvertToSensorWeight(bottom_left)); + bb_data.temperature = TEMPERATURE; + bb_data.battery = 0x83; // Above the threshold for 4 bars target_state->data = bb_data; } @@ -62,39 +78,48 @@ void BalanceBoardExt::Reset() m_reg.calibration = {{// Unused battery calibration 0x01, 0x69, 0x00, 0x00, // Top right 0kg - 0x07, 0xbc, + u8(WEIGHT_0_KG >> 8), u8(WEIGHT_0_KG & 0xFF), // Bottom right 0kg - 0x11, 0x8b, + u8(WEIGHT_0_KG >> 8), u8(WEIGHT_0_KG & 0xFF), // Top left 0kg - 0x06, 0xba, + u8(WEIGHT_0_KG >> 8), u8(WEIGHT_0_KG & 0xFF), // Bottom left 0kg - 0x46, 0x52, + u8(WEIGHT_0_KG >> 8), u8(WEIGHT_0_KG & 0xFF), // Top right 17kg - 0x0e, 0x6e, + u8(WEIGHT_17_KG >> 8), u8(WEIGHT_17_KG & 0xFF), // Bottom right 17kg - 0x18, 0x79}}; + u8(WEIGHT_17_KG >> 8), u8(WEIGHT_17_KG & 0xFF)}}; m_reg.calibration2 = {{// Top left 17kg - 0x0d, 0x5d, + u8(WEIGHT_17_KG >> 8), u8(WEIGHT_17_KG & 0xFF), // Bottom left 17kg - 0x4d, 0x4c, + u8(WEIGHT_17_KG >> 8), u8(WEIGHT_17_KG & 0xFF), // Top right 34kg - 0x15, 0x2e, + u8(WEIGHT_34_KG >> 8), u8(WEIGHT_34_KG & 0xFF), // Bottom right 34kg - 0x1f, 0x71, + u8(WEIGHT_34_KG >> 8), u8(WEIGHT_34_KG & 0xFF), // Top left 34kg - 0x14, 0x07, + u8(WEIGHT_34_KG >> 8), u8(WEIGHT_34_KG & 0xFF), // Bottom left 34kg - 0x54, 0x51, - // Checksum - 0xa9, 0x06, 0xb4, 0xf0}}; - m_reg.calibration3 = {{0x19, 0x01}}; + u8(WEIGHT_34_KG >> 8), u8(WEIGHT_34_KG & 0xFF), + // Checksum - computed later + 0xff, 0xff, 0xff, 0xff}}; + m_reg.calibration3 = {{TEMPERATURE, 0x01}}; - // UpdateCalibrationDataChecksum(m_reg.calibration, CALIBRATION_CHECKSUM_BYTES); + ComputeCalibrationChecksum(); } ControllerEmu::ControlGroup* BalanceBoardExt::GetGroup(BalanceBoardGroup group) { - return nullptr; + switch (group) + { + case BalanceBoardGroup::Balance: + return m_balance; + case BalanceBoardGroup::Weight: + return m_weight; + default: + assert(false); + return nullptr; + } } void BalanceBoardExt::DoState(PointerWrap& p) @@ -104,6 +129,89 @@ void BalanceBoardExt::DoState(PointerWrap& p) void BalanceBoardExt::LoadDefaults(const ControllerInterface& ciface) { + // Balance + m_balance->SetControlExpression(0, "I"); // up + m_balance->SetControlExpression(1, "K"); // down + m_balance->SetControlExpression(2, "J"); // left + m_balance->SetControlExpression(3, "L"); // right + + // Because our defaults use keyboard input, set calibration shape to a square. + m_balance->SetCalibrationFromGate(ControllerEmu::SquareStickGate(.5)); } +u16 BalanceBoardExt::ConvertToSensorWeight(double weight_in_kilos) +{ + constexpr u16 LOW_WEIGHT_DELTA = WEIGHT_17_KG - WEIGHT_0_KG; + constexpr u16 HIGH_WEIGHT_DELTA = WEIGHT_34_KG - WEIGHT_17_KG; + + // Note: this is the weight on a single sensor, so these ranges make more sense + // (if all sensors read 34 kilos, then the overall weight would be 136 kilos or 300 pounds...) + if (weight_in_kilos < 17) + { + return WEIGHT_0_KG + (LOW_WEIGHT_DELTA * weight_in_kilos / 17); + } + else + { + return WEIGHT_17_KG + (HIGH_WEIGHT_DELTA * (weight_in_kilos - 17) / 17); + } +} + +void BalanceBoardExt::ComputeCalibrationChecksum() +{ + std::array data; + + data[0x00] = m_reg.calibration[0x4]; + data[0x01] = m_reg.calibration[0x5]; + data[0x02] = m_reg.calibration[0x6]; + data[0x03] = m_reg.calibration[0x7]; + data[0x04] = m_reg.calibration[0x8]; + data[0x05] = m_reg.calibration[0x9]; + data[0x06] = m_reg.calibration[0xa]; + data[0x07] = m_reg.calibration[0xb]; + data[0x08] = m_reg.calibration[0xc]; + data[0x09] = m_reg.calibration[0xd]; + data[0x0a] = m_reg.calibration[0xe]; + data[0x0b] = m_reg.calibration[0xf]; + data[0x0c] = m_reg.calibration2[0x0]; + data[0x0d] = m_reg.calibration2[0x1]; + data[0x0e] = m_reg.calibration2[0x2]; + data[0x0f] = m_reg.calibration2[0x3]; + data[0x10] = m_reg.calibration2[0x4]; + data[0x11] = m_reg.calibration2[0x5]; + data[0x12] = m_reg.calibration2[0x6]; + data[0x13] = m_reg.calibration2[0x7]; + data[0x14] = m_reg.calibration2[0x8]; + data[0x15] = m_reg.calibration2[0x9]; + data[0x16] = m_reg.calibration2[0xa]; + data[0x17] = m_reg.calibration2[0xb]; + data[0x18] = m_reg.calibration[0]; + data[0x19] = m_reg.calibration[1]; + data[0x1a] = m_reg.calibration3[0]; + data[0x1b] = m_reg.calibration3[1]; + + constexpr u32 CRC_POLYNOMIAL = 0xEDB88320; // Reversed + + u32 result = 0xffffffff; + for (auto byte : data) + { + // This could be cached into a table (and usually is), but since this is called infrequently and + // on a small number of bytes, don't bother. + u32 remainder = (byte ^ (result & 0xff)); + for (auto bit = 0; bit < 8; bit++) + { + if (remainder & 1) + remainder = (remainder >> 1) ^ CRC_POLYNOMIAL; + else + remainder = remainder >> 1; + } + + result = (result >> 8) ^ remainder; + } + result ^= 0xffffffff; + + m_reg.calibration2[0x0c] = u8(result >> 24); + m_reg.calibration2[0x0d] = u8(result >> 16); + m_reg.calibration2[0x0e] = u8(result >> 8); + m_reg.calibration2[0x0f] = u8(result); +} } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/BalanceBoard.h b/Source/Core/Core/HW/WiimoteEmu/Extension/BalanceBoard.h index db8e5a8bdb..664692c90e 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/BalanceBoard.h +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/BalanceBoard.h @@ -11,13 +11,17 @@ namespace ControllerEmu { +class AnalogStick; class ControlGroup; +class Triggers; } // namespace ControllerEmu namespace WiimoteEmu { enum class BalanceBoardGroup { + Balance, + Weight }; class BalanceBoardExt : public Extension1stParty @@ -37,6 +41,8 @@ public: BalanceBoardExt(); + static constexpr float DEFAULT_WEIGHT = 63.5; // kilograms; no specific meaning to this value + void BuildDesiredExtensionState(DesiredExtensionState* target_state) override; void Update(const DesiredExtensionState& target_state) override; void Reset() override; @@ -46,6 +52,21 @@ public: void LoadDefaults(const ControllerInterface& ciface) override; + // Use the same calibration data for all sensors. + // Wii Fit appears to convert to this internally (so it doesn't care about differences smaller + // than a gram). Normal balance boards tend to be less precise, usually around 10 grams. + static constexpr u16 WEIGHT_0_KG = 0; + static constexpr u16 WEIGHT_17_KG = 17000; + static constexpr u16 WEIGHT_34_KG = 34000; + // Chosen arbitrarily from the value for pokechu22's board. As long as the calibration and + // actual temperatures match, the value here doesn't matter. + static constexpr u8 TEMPERATURE = 0x19; + private: + u16 ConvertToSensorWeight(double weight_in_kilos); + void ComputeCalibrationChecksum(); + + ControllerEmu::AnalogStick* m_balance; + ControllerEmu::Triggers* m_weight; }; } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 501b5a5972..7308607e4c 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -432,6 +432,13 @@ ControllerEmu::ControlGroup* Wiimote::GetShinkansenGroup(ShinkansenGroup group) ->GetGroup(group); } +ControllerEmu::ControlGroup* Wiimote::GetBalanceBoardGroup(BalanceBoardGroup group) const +{ + return static_cast( + m_attachments->GetAttachmentList()[ExtensionNumber::BALANCE_BOARD].get()) + ->GetGroup(group); +} + bool Wiimote::ProcessExtensionPortEvent() { // WiiBrew: Following a connection or disconnection event on the Extension Port, diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index e50a6efbc9..a542d6abce 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -72,6 +72,7 @@ enum class UDrawTabletGroup; enum class DrawsomeTabletGroup; enum class TaTaConGroup; enum class ShinkansenGroup; +enum class BalanceBoardGroup; template void UpdateCalibrationDataChecksum(T& data, int cksum_bytes) @@ -155,6 +156,7 @@ public: ControllerEmu::ControlGroup* GetDrawsomeTabletGroup(DrawsomeTabletGroup group) const; ControllerEmu::ControlGroup* GetTaTaConGroup(TaTaConGroup group) const; ControllerEmu::ControlGroup* GetShinkansenGroup(ShinkansenGroup group) const; + ControllerEmu::ControlGroup* GetBalanceBoardGroup(BalanceBoardGroup group) const; u8 GetWiimoteDeviceIndex() const override; void SetWiimoteDeviceIndex(u8 index) override; diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp index ed850181f9..a9d87bcf80 100644 --- a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp @@ -9,6 +9,7 @@ #include #include "Core/HW/Wiimote.h" +#include "Core/HW/WiimoteEmu/Extension/BalanceBoard.h" #include "Core/HW/WiimoteEmu/Extension/Classic.h" #include "Core/HW/WiimoteEmu/Extension/DrawsomeTablet.h" #include "Core/HW/WiimoteEmu/Extension/Drums.h" @@ -34,6 +35,7 @@ WiimoteEmuExtension::WiimoteEmuExtension(MappingWindow* window) : MappingWidget( CreateDrawsomeTabletLayout(); CreateTaTaConLayout(); CreateShinkansenLayout(); + CreateBalanceBoardLayout(); CreateMainLayout(); ChangeExtensionType(WiimoteEmu::ExtensionNumber::NONE); @@ -241,6 +243,23 @@ void WiimoteEmuExtension::CreateShinkansenLayout() m_shinkansen_box->setLayout(hbox); } +void WiimoteEmuExtension::CreateBalanceBoardLayout() +{ + auto* layout = new QGridLayout(); + m_balance_board_box = new QGroupBox(tr("Balance Board"), this); + + layout->addWidget( + CreateGroupBox(tr("Balance"), Wiimote::GetBalanceBoardGroup( + GetPort(), WiimoteEmu::BalanceBoardGroup::Balance)), + 0, 0); + layout->addWidget( + CreateGroupBox(tr("Balance"), Wiimote::GetBalanceBoardGroup( + GetPort(), WiimoteEmu::BalanceBoardGroup::Weight)), + 0, 1); + + m_balance_board_box->setLayout(layout); +} + void WiimoteEmuExtension::CreateMainLayout() { m_main_layout = new QHBoxLayout(); @@ -255,6 +274,7 @@ void WiimoteEmuExtension::CreateMainLayout() m_main_layout->addWidget(m_drawsome_tablet_box); m_main_layout->addWidget(m_tatacon_box); m_main_layout->addWidget(m_shinkansen_box); + m_main_layout->addWidget(m_balance_board_box); setLayout(m_main_layout); } @@ -288,4 +308,5 @@ void WiimoteEmuExtension::ChangeExtensionType(u32 type) m_drawsome_tablet_box->setHidden(type != ExtensionNumber::DRAWSOME_TABLET); m_tatacon_box->setHidden(type != ExtensionNumber::TATACON); m_shinkansen_box->setHidden(type != ExtensionNumber::SHINKANSEN); + m_balance_board_box->setHidden(type != ExtensionNumber::BALANCE_BOARD); } diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.h b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.h index 43b5ebbdd5..cde8c94304 100644 --- a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.h +++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.h @@ -34,6 +34,7 @@ private: void CreateDrawsomeTabletLayout(); void CreateTaTaConLayout(); void CreateShinkansenLayout(); + void CreateBalanceBoardLayout(); void CreateMainLayout(); // Main @@ -48,4 +49,5 @@ private: QGroupBox* m_drawsome_tablet_box; QGroupBox* m_tatacon_box; QGroupBox* m_shinkansen_box; + QGroupBox* m_balance_board_box; };