diff --git a/sdk/SMX.h b/sdk/SMX.h index 1336e77..0e7a6b6 100644 --- a/sdk/SMX.h +++ b/sdk/SMX.h @@ -2,6 +2,7 @@ #define SMX_H #include +#include // for offsetof #ifdef SMX_EXPORTS #define SMX_API extern "C" __declspec(dllexport) @@ -151,19 +152,54 @@ enum SMXConfigFlags { // If set, panels are using FSRs, otherwise load cells. PlatformFlags_FSR = 1 << 1, }; +#pragma pack(push, 1) + +struct packed_sensor_settings_t { + // Load cell thresholds: + uint8_t loadCellLowThreshold; + uint8_t loadCellHighThreshold; + + // FSR thresholds: + uint16_t fsrLowThreshold[4]; + uint16_t fsrHighThreshold[4]; + + // This must be left unchanged. + uint16_t reserved; +}; + +static_assert(sizeof(packed_sensor_settings_t) == 20, "Incorrect packed_sensor_settings_t size"); // The configuration for a connected controller. This can be retrieved with SMX_GetConfig // and modified with SMX_SetConfig. // // The order and packing of this struct corresponds to the configuration packet sent to // the master controller, so it must not be changed. -#pragma pack(push, 1) struct SMXConfig { - // These fields are unused and must be left at their existing values. - uint8_t unused1 = 0xFF, unused2 = 0xFF; - uint8_t unused3 = 0xFF, unused4 = 0xFF; - uint8_t unused5 = 0xFF, unused6 = 0xFF; + // The firmware version of the master controller. Where supported (version 2 and up), this + // will always read back the firmware version. This will default to 0xFF on version 1, and + // we'll always write 0xFF here so it doesn't change on that firmware version. + // + // We don't need this since we can read the "I" command which also reports the version, but + // this allows panels to also know the master version. + uint8_t masterVersion = 0xFF; + + // The version of this config packet. This can be used by the firmware to know which values + // have been filled in. Any values not filled in will always be 0xFF, which can be tested + // for, but that doesn't work for values where 0xFF is a valid value. This value is unrelated + // to the firmware version, and just indicates which fields in this packet have been set. + // Note that we don't need to increase this any time we add a field, only when it's important + // that we be able to tell if a field is set or not. + // + // Versions: + // - 0xFF: This is a config packet from before configVersion was added. + // - 0x00: configVersion added + // - 0x02: panelThreshold0Low through panelThreshold8High added + // - 0x03: debounceDelayMs added + uint8_t configVersion = 0x05; + + // Packed flags (masterVersion >= 4). + uint8_t flags = 0; // Panel thresholds are labelled by their numpad position, eg. Panel8 is up. // If m_iFirmwareVersion is 1, Panel7 corresponds to all of up, down, left and @@ -171,21 +207,18 @@ struct SMXConfig // later firmware versions, each panel is configured independently. // // Setting a value to 0xFF disables that threshold. - uint16_t masterDebounceMilliseconds = 0; - uint8_t panelThreshold7Low = 0xFF, panelThreshold7High = 0xFF; // was "cardinal" - uint8_t panelThreshold4Low = 0xFF, panelThreshold4High = 0xFF; // was "center" - uint8_t panelThreshold2Low = 0xFF, panelThreshold2High = 0xFF; // was "corner" // These are internal tunables and should be left unchanged. + uint16_t debounceNodelayMilliseconds = 0; + uint16_t debounceDelayMs = 0; uint16_t panelDebounceMicroseconds = 4000; - uint16_t autoCalibrationPeriodMilliseconds = 1000; uint8_t autoCalibrationMaxDeviation = 100; uint8_t badSensorMinimumDelaySeconds = 15; uint16_t autoCalibrationAveragesPerUpdate = 60; + uint16_t autoCalibrationSamplesPerAverage = 500; - uint8_t unused7 = 0xFF, unused8 = 0xFF; - - uint8_t panelThreshold1Low = 0xFF, panelThreshold1High = 0xFF; // was "up" + // The maximum tare value to calibrate to (except on startup). + uint16_t autoCalibrationMaxTare; // Which sensors on each panel to enable. This can be used to disable sensors that // we know aren't populated. This is packed, with four sensors on two pads per byte: @@ -202,76 +235,34 @@ struct SMXConfig // range. uint8_t stepColor[3*9]; - // The rotation of the panel, where 0 is the standard rotation, 1 means the panel is - // rotated right 90 degrees, 2 is rotated 180 degrees, and 3 is rotated 270 degrees. - // This value is unused. - uint8_t panelRotation; - - // This is an internal tunable that should be left unchanged. - uint16_t autoCalibrationSamplesPerAverage = 500; - - // The firmware version of the master controller. Where supported (version 2 and up), this - // will always read back the firmware version. This will default to 0xFF on version 1, and - // we'll always write 0xFF here so it doesn't change on that firmware version. - // - // We don't need this since we can read the "I" command which also reports the version, but - // this allows panels to also know the master version. - uint8_t masterVersion = 0xFF; - - // The version of this config packet. This can be used by the firmware to know which values - // have been filled in. Any values not filled in will always be 0xFF, which can be tested - // for, but that doesn't work for values where 0xFF is a valid value. This value is unrelated - // to the firmware version, and just indicates which fields in this packet have been set. - // Note that we don't need to increase this any time we add a field, only when it's important - // that we be able to tell if a field is set or not. - // - // Versions: - // - 0xFF: This is a config packet from before configVersion was added. - // - 0x00: configVersion added - // - 0x02: panelThreshold0Low through panelThreshold8High added - // - 0x03: debounceDelayMs added - uint8_t configVersion = 0x03; - - // The remaining thresholds (configVersion >= 2). - uint8_t unused9[10]; - uint8_t panelThreshold0Low, panelThreshold0High; - uint8_t panelThreshold3Low, panelThreshold3High; - uint8_t panelThreshold5Low, panelThreshold5High; - uint8_t panelThreshold6Low, panelThreshold6High; - uint8_t panelThreshold8Low, panelThreshold8High; - - // Master delay debouncing (version >= 3). If enabled, this will add a - // corresponding delay to inputs, which the game needs to compensate for. - // This is disabled by default. - uint16_t debounceDelayMs = 0; - - // Packed flags (masterVersion >= 4). - uint8_t flags = 0; - - // Thresholds when in FSR mode. Note that these are 16-bit thresholds, compared - // to the 8-bit load cell thresholds. - uint16_t individualPanelFSRLow[9]; - uint16_t individualPanelFSRHigh[9]; - // The default color to set the platform LED strip to. uint8_t platformStripColor[3]; - // The maximum tare value to calibrate to (except on startup). - uint16_t autoCalibrationMaxTare; - // Which panels to enable auto-lighting for. Disabled panels will be unlit. // 0x01 = panel 0, 0x02 = panel 1, 0x04 = panel 2, etc. This only affects // the master controller's built-in auto lighting and not lights data send // from the SDK. uint16_t autoLightPanelMask; + // The rotation of the panel, where 0 is the standard rotation, 1 means the panel is + // rotated right 90 degrees, 2 is rotated 180 degrees, and 3 is rotated 270 degrees. + // This value is unused. + uint8_t panelRotation; + + // Per-panel sensor settings: + packed_sensor_settings_t panelSettings[9]; + + // These are internal tunables and should be left unchanged. + uint8_t preDetailsDelayMilliseconds; + // Pad the struct to 250 bytes. This keeps this struct size from changing // as we add fields, so the ABI doesn't change. Applications should leave // any data in here unchanged when calling SMX_SetConfig. - uint8_t padding[120]; + uint8_t padding[13]; }; #pragma pack(pop) -static_assert(offsetof(SMXConfig, padding) == 130, "Incorrect padding alignment"); + +static_assert(offsetof(SMXConfig, padding) == 237, "Incorrect padding alignment"); static_assert(sizeof(SMXConfig) == 250, "Expected 250 bytes"); // The values (except for Off) correspond with the protocol and must not be changed. diff --git a/sdk/Windows/SMXDevice.cpp b/sdk/Windows/SMXDevice.cpp index d6f850e..2304559 100644 --- a/sdk/Windows/SMXDevice.cpp +++ b/sdk/Windows/SMXDevice.cpp @@ -200,7 +200,11 @@ void SMX::SMXDevice::FactoryReset() // Send a factory reset command, and then read the new configuration. LockMutex Lock(m_Lock); SendCommandLocked("f\n"); - SendCommandLocked("g\n", [&](string response) { + + SMXDeviceInfo deviceInfo = m_pConnection->GetDeviceInfo(); + + SendCommandLocked(deviceInfo.m_iFirmwareVersion >= 5? "G":"g\n", + [&](string response) { // We now have the new configuration. m_Lock.AssertLockedByCurrentThread(); CallUpdateCallback(SMXUpdateCallback_FactoryResetCommandComplete); @@ -260,7 +264,10 @@ void SMX::SMXDevice::HandlePackets() HandleSensorTestDataResponse(buf); break; + // 'g' is sent by firmware versions 1-4. Version 5 and newer send 'G', to ensure + // older code doesn't misinterpret the modified config packet format. case 'g': + case 'G': { // This command reads back the configuration we wrote with 'w', or the defaults if // we haven't written any. @@ -310,8 +317,11 @@ void SMX::SMXDevice::SendConfig() if(m_bWaitingForConfigResponse) return; - // Write configuration command: - string sData = ssprintf("w"); + SMXDeviceInfo deviceInfo = m_pConnection->GetDeviceInfo(); + + // Write configuration command. This is "w" in versions 1-4, and "W" in versions 5 + // and newer. + string sData = ssprintf(deviceInfo.m_iFirmwareVersion >= 5? "W":"w"); int8_t iSize = sizeof(SMXConfig); // Firmware through version 3 allowed config packets up to 128 bytes. Additions @@ -344,8 +354,10 @@ void SMX::SMXDevice::SendConfig() m_bWaitingForConfigResponse = true; // After we write the configuration, read back the updated configuration to - // verify it. - SendCommandLocked("g\n", [this](string response) { + // verify it. This command is "g" in versions 1-4, and "G" in versions 5 and + // newer. + SendCommandLocked( + deviceInfo.m_iFirmwareVersion >= 5? "G":"g\n", [this](string response) { m_bWaitingForConfigResponse = false; }); } @@ -387,9 +399,11 @@ void SMX::SMXDevice::CheckActive() m_pConnection->SetActive(true); - // Read the current configuration. The device will return a "g" response containing - // its current SMXConfig. - SendCommandLocked("g\n"); + SMXDeviceInfo deviceInfo = m_pConnection->GetDeviceInfo(); + + // Read the current configuration. The device will return a "g" or "G" response + // containing its current SMXConfig. + SendCommandLocked(deviceInfo.m_iFirmwareVersion >= 5? "G":"g\n"); } // Check if we need to request test mode data. diff --git a/smx-config/ConfigPresets.cs b/smx-config/ConfigPresets.cs index 67cbeee..f8d4059 100644 --- a/smx-config/ConfigPresets.cs +++ b/smx-config/ConfigPresets.cs @@ -34,33 +34,18 @@ namespace smx_config // Return true if the config matches, only comparing values that we set in presets. static private bool SamePreset(SMX.SMXConfig config1, SMX.SMXConfig config2) { - // These aren't arrays for compatibility reasons. - if( config1.panelThreshold0High != config2.panelThreshold0High || - config1.panelThreshold1High != config2.panelThreshold1High || - config1.panelThreshold2High != config2.panelThreshold2High || - config1.panelThreshold3High != config2.panelThreshold3High || - config1.panelThreshold4High != config2.panelThreshold4High || - config1.panelThreshold5High != config2.panelThreshold5High || - config1.panelThreshold6High != config2.panelThreshold6High || - config1.panelThreshold7High != config2.panelThreshold7High || - config1.panelThreshold8High != config2.panelThreshold8High) - return false; - if( config1.panelThreshold0Low != config2.panelThreshold0Low || - config1.panelThreshold1Low != config2.panelThreshold1Low || - config1.panelThreshold2Low != config2.panelThreshold2Low || - config1.panelThreshold3Low != config2.panelThreshold3Low || - config1.panelThreshold4Low != config2.panelThreshold4Low || - config1.panelThreshold5Low != config2.panelThreshold5Low || - config1.panelThreshold6Low != config2.panelThreshold6Low || - config1.panelThreshold7Low != config2.panelThreshold7Low || - config1.panelThreshold8Low != config2.panelThreshold8Low) - return false; - - for(int i = 0; i < 9; ++i) + for(int panel = 0; panel < 9; ++panel) { - if(config1.individualPanelFSRLow[i] != config2.individualPanelFSRLow[i] || - config1.individualPanelFSRHigh[i] != config2.individualPanelFSRHigh[i]) - return false; + if(config1.panelSettings[panel].loadCellLowThreshold != config2.panelSettings[panel].loadCellLowThreshold || + config1.panelSettings[panel].loadCellHighThreshold != config2.panelSettings[panel].loadCellHighThreshold) + return false; + + for(int sensor = 0; sensor < 4; ++sensor) + { + if(config1.panelSettings[panel].fsrLowThreshold[sensor] != config2.panelSettings[panel].fsrLowThreshold[sensor] || + config1.panelSettings[panel].fsrHighThreshold[sensor] != config2.panelSettings[panel].fsrHighThreshold[sensor]) + return false; + } } return true; @@ -76,79 +61,56 @@ namespace smx_config } } + static private void SetPreset(ref SMX.SMXConfig config, + byte loadCellLow, byte loadCellHigh, byte loadCellLowCenter, byte loadCellHighCenter, + UInt16 fsrLow, UInt16 fsrHigh, UInt16 fsrLowCenter, UInt16 fsrHighCenter) + { + for(int panel = 0; panel < 9; ++panel) + { + config.panelSettings[panel].loadCellLowThreshold = loadCellLow; + config.panelSettings[panel].loadCellHighThreshold = loadCellHigh; + } + + // Center: + config.panelSettings[4].loadCellLowThreshold = loadCellLowCenter; + config.panelSettings[4].loadCellHighThreshold = loadCellHighCenter; + + for(int panel = 0; panel < 9; ++panel) + { + for(int sensor = 0; sensor < 4; ++sensor) + { + config.panelSettings[panel].fsrLowThreshold[sensor] = fsrLow; + config.panelSettings[panel].fsrHighThreshold[sensor] = fsrHigh; + } + } + + // Center: + for(int sensor = 0; sensor < 4; ++sensor) + { + config.panelSettings[4].fsrLowThreshold[sensor] = fsrLowCenter; + config.panelSettings[4].fsrHighThreshold[sensor] = fsrHighCenter; + } + } + static private void SetHighPreset(ref SMX.SMXConfig config) { - config.panelThreshold7Low = // cardinal - config.panelThreshold1Low = // up - config.panelThreshold2Low = 20; // corner - config.panelThreshold7High = // cardinal - config.panelThreshold1High = // up - config.panelThreshold2High = 25; // corner - - config.panelThreshold4Low = 20; // center - config.panelThreshold4High = 30; - - config.individualPanelFSRLow[7] = // cardinal - config.individualPanelFSRLow[1] = // up - config.individualPanelFSRLow[2] = 275; // corner - config.individualPanelFSRHigh[7] = // cardinal - config.individualPanelFSRHigh[1] = // up - config.individualPanelFSRHigh[2] = 300; // corner - - config.individualPanelFSRLow[4] = 400; // center - config.individualPanelFSRHigh[4] = 500; - - SyncUnifiedThresholds(ref config); + SetPreset(ref config, + 20, 25, 20, 30, + 275, 300, 275, 300); } static private void SetNormalPreset(ref SMX.SMXConfig config) { - config.panelThreshold7Low = // cardinal - config.panelThreshold1Low = // up - config.panelThreshold2Low = 33; // corner - config.panelThreshold7High = // cardinal - config.panelThreshold1High = // up - config.panelThreshold2High = 42; // corner - - config.panelThreshold4Low = 35; // center - config.panelThreshold4High = 60; - - config.individualPanelFSRLow[7] = // cardinal - config.individualPanelFSRLow[1] = // up - config.individualPanelFSRLow[2] = 650; // corner - config.individualPanelFSRHigh[7] = // cardinal - config.individualPanelFSRHigh[1] = // up - config.individualPanelFSRHigh[2] = 700; // corner - - config.individualPanelFSRLow[4] = 845; // center - config.individualPanelFSRHigh[4] = 895; - - SyncUnifiedThresholds(ref config); + SetPreset(ref config, + 33, 42, 35, 60, + 650, 700, 845, 895); } static private void SetLowPreset(ref SMX.SMXConfig config) { - config.panelThreshold7Low = // cardinal - config.panelThreshold1Low = // up - config.panelThreshold2Low = 70; // corner - config.panelThreshold7High = // cardinal - config.panelThreshold1High = // up - config.panelThreshold2High = 80; // corner - - config.panelThreshold4Low = 100; // center - config.panelThreshold4High = 120; - - config.individualPanelFSRLow[7] = // cardinal - config.individualPanelFSRLow[1] = // up - config.individualPanelFSRLow[2] = 800; // corner - config.individualPanelFSRHigh[7] = // cardinal - config.individualPanelFSRHigh[1] = // up - config.individualPanelFSRHigh[2] = 875; // corner - - config.individualPanelFSRLow[4] = 845; // center - config.individualPanelFSRHigh[4] = 895; - - SyncUnifiedThresholds(ref config); + SetPreset(ref config, + 70, 80, 100, 120, + 800, 875, 845, 895); } // The simplified configuration scheme sets thresholds for up, center, cardinal directions @@ -158,18 +120,21 @@ namespace smx_config static public void SyncUnifiedThresholds(ref SMX.SMXConfig config) { // left = right = down (cardinal) - config.panelThreshold3Low = config.panelThreshold5Low = config.panelThreshold7Low; - config.panelThreshold3High = config.panelThreshold5High = config.panelThreshold7High; + config.panelSettings[3].loadCellLowThreshold = config.panelSettings[5].loadCellLowThreshold = config.panelSettings[7].loadCellLowThreshold; + config.panelSettings[3].loadCellHighThreshold = config.panelSettings[5].loadCellHighThreshold = config.panelSettings[7].loadCellHighThreshold; // UL = DL = DR = UR (corners) - config.panelThreshold0Low = config.panelThreshold6Low = config.panelThreshold8Low = config.panelThreshold2Low; - config.panelThreshold0High = config.panelThreshold6High = config.panelThreshold8High = config.panelThreshold2High; + config.panelSettings[0].loadCellLowThreshold = config.panelSettings[6].loadCellLowThreshold = config.panelSettings[8].loadCellLowThreshold = config.panelSettings[2].loadCellLowThreshold; + config.panelSettings[0].loadCellHighThreshold = config.panelSettings[6].loadCellHighThreshold = config.panelSettings[8].loadCellHighThreshold = config.panelSettings[2].loadCellHighThreshold; // Do the same for FSR thresholds. - config.individualPanelFSRLow[3] = config.individualPanelFSRLow[5] = config.individualPanelFSRLow[7]; - config.individualPanelFSRHigh[3] = config.individualPanelFSRHigh[5] = config.individualPanelFSRHigh[7]; - config.individualPanelFSRLow[0] = config.individualPanelFSRLow[6] = config.individualPanelFSRLow[8] = config.individualPanelFSRLow[2]; - config.individualPanelFSRHigh[0] = config.individualPanelFSRHigh[6] = config.individualPanelFSRHigh[8] = config.individualPanelFSRHigh[2]; + for(int sensor = 0; sensor < 4; ++sensor) + { + config.panelSettings[3].fsrLowThreshold[sensor] = config.panelSettings[5].fsrLowThreshold[sensor] = config.panelSettings[7].fsrLowThreshold[sensor]; + config.panelSettings[3].fsrHighThreshold[sensor] = config.panelSettings[5].fsrHighThreshold[sensor] = config.panelSettings[7].fsrHighThreshold[sensor]; + config.panelSettings[0].fsrLowThreshold[sensor] = config.panelSettings[6].fsrLowThreshold[sensor] = config.panelSettings[8].fsrLowThreshold[sensor] = config.panelSettings[2].fsrLowThreshold[sensor]; + config.panelSettings[0].fsrHighThreshold[sensor] = config.panelSettings[6].fsrHighThreshold[sensor] = config.panelSettings[8].fsrHighThreshold[sensor] = config.panelSettings[2].fsrHighThreshold[sensor]; + } } // Return true if the panel thresholds are already synced, so SyncUnifiedThresholds would diff --git a/smx-config/Helpers.cs b/smx-config/Helpers.cs index 44914c9..87e9c42 100644 --- a/smx-config/Helpers.cs +++ b/smx-config/Helpers.cs @@ -528,27 +528,13 @@ namespace smx_config { Dictionary dict = new Dictionary(); List panelLowThresholds = new List(); - panelLowThresholds.Add(config.panelThreshold0Low); - panelLowThresholds.Add(config.panelThreshold1Low); - panelLowThresholds.Add(config.panelThreshold2Low); - panelLowThresholds.Add(config.panelThreshold3Low); - panelLowThresholds.Add(config.panelThreshold4Low); - panelLowThresholds.Add(config.panelThreshold5Low); - panelLowThresholds.Add(config.panelThreshold6Low); - panelLowThresholds.Add(config.panelThreshold7Low); - panelLowThresholds.Add(config.panelThreshold8Low); + for(int panel = 0; panel < 9; ++panel) + panelLowThresholds.Add(config.panelSettings[panel].loadCellLowThreshold); dict.Add("panelLowThresholds", panelLowThresholds); List panelHighThresholds = new List(); - panelHighThresholds.Add(config.panelThreshold0High); - panelHighThresholds.Add(config.panelThreshold1High); - panelHighThresholds.Add(config.panelThreshold2High); - panelHighThresholds.Add(config.panelThreshold3High); - panelHighThresholds.Add(config.panelThreshold4High); - panelHighThresholds.Add(config.panelThreshold5High); - panelHighThresholds.Add(config.panelThreshold6High); - panelHighThresholds.Add(config.panelThreshold7High); - panelHighThresholds.Add(config.panelThreshold8High); + for(int panel = 0; panel < 9; ++panel) + panelLowThresholds.Add(config.panelSettings[panel].loadCellHighThreshold); dict.Add("panelHighThresholds", panelHighThresholds); // Store the enabled panel mask as a simple list of which panels are selected. @@ -582,26 +568,12 @@ namespace smx_config // Read the thresholds. If any values are missing, we'll leave the value in config alone. List newPanelLowThresholds = dict.Get("panelLowThresholds", new List()); - config.panelThreshold0Low = newPanelLowThresholds.Get(0, config.panelThreshold0Low); - config.panelThreshold1Low = newPanelLowThresholds.Get(1, config.panelThreshold1Low); - config.panelThreshold2Low = newPanelLowThresholds.Get(2, config.panelThreshold2Low); - config.panelThreshold3Low = newPanelLowThresholds.Get(3, config.panelThreshold3Low); - config.panelThreshold4Low = newPanelLowThresholds.Get(4, config.panelThreshold4Low); - config.panelThreshold5Low = newPanelLowThresholds.Get(5, config.panelThreshold5Low); - config.panelThreshold6Low = newPanelLowThresholds.Get(6, config.panelThreshold6Low); - config.panelThreshold7Low = newPanelLowThresholds.Get(7, config.panelThreshold7Low); - config.panelThreshold8Low = newPanelLowThresholds.Get(8, config.panelThreshold8Low); + for(int panel = 0; panel < 9; ++panel) + config.panelSettings[panel].loadCellLowThreshold = newPanelLowThresholds.Get(panel, config.panelSettings[panel].loadCellLowThreshold); List newPanelHighThresholds = dict.Get("panelHighThresholds", new List()); - config.panelThreshold0High = newPanelHighThresholds.Get(0, config.panelThreshold0High); - config.panelThreshold1High = newPanelHighThresholds.Get(1, config.panelThreshold1High); - config.panelThreshold2High = newPanelHighThresholds.Get(2, config.panelThreshold2High); - config.panelThreshold3High = newPanelHighThresholds.Get(3, config.panelThreshold3High); - config.panelThreshold4High = newPanelHighThresholds.Get(4, config.panelThreshold4High); - config.panelThreshold5High = newPanelHighThresholds.Get(5, config.panelThreshold5High); - config.panelThreshold6High = newPanelHighThresholds.Get(6, config.panelThreshold6High); - config.panelThreshold7High = newPanelHighThresholds.Get(7, config.panelThreshold7High); - config.panelThreshold8High = newPanelHighThresholds.Get(8, config.panelThreshold8High); + for(int panel = 0; panel < 9; ++panel) + config.panelSettings[panel].loadCellHighThreshold = newPanelHighThresholds.Get(panel, config.panelSettings[panel].loadCellHighThreshold); List enabledPanelList = dict.Get>("enabledPanels", null); if(enabledPanelList != null) diff --git a/smx-config/SMX.cs b/smx-config/SMX.cs index 5e4d84b..b3706d6 100644 --- a/smx-config/SMX.cs +++ b/smx-config/SMX.cs @@ -27,22 +27,38 @@ namespace SMX PlatformFlags_FSR = 1 << 1, }; + public struct PackedSensorSettings { + // Load cell thresholds: + public Byte loadCellLowThreshold; + public Byte loadCellHighThreshold; + + // FSR thresholds (16-bit): + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public UInt16[] fsrLowThreshold; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public UInt16[] fsrHighThreshold; + + // This must be left unchanged. + public UInt16 reserved; + }; + [StructLayout(LayoutKind.Sequential, Pack=1)] public struct SMXConfig { - public Byte unused1, unused2; - public Byte unused3, unused4; - public Byte unused5, unused6; - public UInt16 masterDebounceMilliseconds; - public Byte panelThreshold7Low, panelThreshold7High; // was "cardinal" - public Byte panelThreshold4Low, panelThreshold4High; // was "center" - public Byte panelThreshold2Low, panelThreshold2High; // was "corner" + public Byte masterVersion; + public Byte configVersion; + + // Packed flags (SMXConfigFlags). + public Byte flags; + + public UInt16 debounceNodelayMilliseconds; + public UInt16 debounceDelayMs; public UInt16 panelDebounceMicroseconds; - public UInt16 autoCalibrationPeriodMilliseconds; public Byte autoCalibrationMaxDeviation; public Byte badSensorMinimumDelaySeconds; public UInt16 autoCalibrationAveragesPerUpdate; - public Byte unused7, unused8; - public Byte panelThreshold1Low, panelThreshold1High; // was "up" + public UInt16 autoCalibrationSamplesPerAverage; + public UInt16 autoCalibrationMaxTare; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public Byte[] enabledSensors; @@ -52,27 +68,28 @@ namespace SMX [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3*9)] public Byte[] stepColor; + // The default color to set the platform LED strip to. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public Byte[] platformStripColor; + + // Which panels to enable auto-lighting for. Disabled panels will be unlit. + // 0x01 = panel 0, 0x02 = panel 1, 0x04 = panel 2, etc. This only affects + // the master controller's built-in auto lighting and not lights data send + // from the SDK. + public UInt16 autoLightPanelMask; + public Byte panelRotation; - public UInt16 autoCalibrationSamplesPerAverage; - public Byte masterVersion; - public Byte configVersion; - // The remaining thresholds (configVersion >= 2). - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public Byte[] unused9; - public Byte panelThreshold0Low, panelThreshold0High; - public Byte panelThreshold3Low, panelThreshold3High; - public Byte panelThreshold5Low, panelThreshold5High; - public Byte panelThreshold6Low, panelThreshold6High; - public Byte panelThreshold8Low, panelThreshold8High; - - // Master delay debouncing (version >= 3). If enabled, this will add a - // corresponding delay to inputs, which the game needs to compensate for. - // This is disabled by default. - public UInt16 debounceDelayMs; + // Per-panel sensor settings: + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] + public PackedSensorSettings[] panelSettings; - // Packed flags (SMXConfigFlags). - public Byte flags; + // These are internal tunables and should be left unchanged. + public Byte preDetailsDelayMilliseconds; + + // Pad this struct to exactly 250 bytes. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] + public Byte[] padding; // It would be simpler to set flags to [MarshalAs(UnmanagedType.U8)], but // that doesn't work. @@ -92,30 +109,6 @@ namespace SMX return masterVersion >= 4 && (configFlags & SMXConfigFlags.PlatformFlags_FSR) != 0; } - // Thresholds when in FSR mode. Note that these are 16-bit thresholds, compared - // to the 8-bit load cell thresholds. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] - public UInt16[] individualPanelFSRLow; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] - public UInt16[] individualPanelFSRHigh; - - // The default color to set the platform LED strip to. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public Byte[] platformStripColor; - - // The maximum tare value to calibrate to (except on startup). - public UInt16 autoCalibrationMaxTare; - - // Which panels to enable auto-lighting for. Disabled panels will be unlit. - // 0x01 = panel 0, 0x02 = panel 1, 0x04 = panel 2, etc. This only affects - // the master controller's built-in auto lighting and not lights data send - // from the SDK. - public UInt16 autoLightPanelMask; - - // Pad this struct to exactly 250 bytes. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 120)] - public Byte[] padding; - // enabledSensors is a mask of which panels are enabled. Return this as an array // for convenience. public bool[] GetEnabledPanels() @@ -167,60 +160,47 @@ namespace SMX // The layout of this structure (and the underlying C struct) matches the firmware configuration // data. This is a bit inconvenient for the panel thresholds which aren't contiguous, so these // helpers just convert them to and from arrays. + // XXX: used? public Byte[] GetLowThresholds() { return new Byte[] { - panelThreshold0Low, - panelThreshold1Low, - panelThreshold2Low, - panelThreshold3Low, - panelThreshold4Low, - panelThreshold5Low, - panelThreshold6Low, - panelThreshold7Low, - panelThreshold8Low, + panelSettings[0].loadCellLowThreshold, + panelSettings[1].loadCellLowThreshold, + panelSettings[2].loadCellLowThreshold, + panelSettings[3].loadCellLowThreshold, + panelSettings[4].loadCellLowThreshold, + panelSettings[5].loadCellLowThreshold, + panelSettings[6].loadCellLowThreshold, + panelSettings[7].loadCellLowThreshold, + panelSettings[8].loadCellLowThreshold, }; } public Byte[] GetHighThresholds() { return new Byte[] { - panelThreshold0High, - panelThreshold1High, - panelThreshold2High, - panelThreshold3High, - panelThreshold4High, - panelThreshold5High, - panelThreshold6High, - panelThreshold7High, - panelThreshold8High, + panelSettings[0].loadCellHighThreshold, + panelSettings[1].loadCellHighThreshold, + panelSettings[2].loadCellHighThreshold, + panelSettings[3].loadCellHighThreshold, + panelSettings[4].loadCellHighThreshold, + panelSettings[5].loadCellHighThreshold, + panelSettings[6].loadCellHighThreshold, + panelSettings[7].loadCellHighThreshold, + panelSettings[8].loadCellHighThreshold, }; } public void SetLowThresholds(Byte[] values) { - panelThreshold0Low = values[0]; - panelThreshold1Low = values[1]; - panelThreshold2Low = values[2]; - panelThreshold3Low = values[3]; - panelThreshold4Low = values[4]; - panelThreshold5Low = values[5]; - panelThreshold6Low = values[6]; - panelThreshold7Low = values[7]; - panelThreshold8Low = values[8]; + for(int panel = 0; panel < 9; ++panel) + panelSettings[panel].loadCellLowThreshold = values[panel]; } public void SetHighThresholds(Byte[] values) { - panelThreshold0High = values[0]; - panelThreshold1High = values[1]; - panelThreshold2High = values[2]; - panelThreshold3High = values[3]; - panelThreshold4High = values[4]; - panelThreshold5High = values[5]; - panelThreshold6High = values[6]; - panelThreshold7High = values[7]; - panelThreshold8High = values[8]; + for(int panel = 0; panel < 9; ++panel) + panelSettings[panel].loadCellHighThreshold = values[panel]; } // Create an empty SMXConfig. @@ -229,10 +209,13 @@ namespace SMX SMXConfig result = new SMXConfig(); result.enabledSensors = new Byte[5]; result.stepColor = new Byte[3*9]; - result.individualPanelFSRLow = new UInt16[9]; - result.individualPanelFSRHigh = new UInt16[9]; + result.panelSettings = new PackedSensorSettings[9]; result.platformStripColor = new Byte[3]; - + for(int panel = 0; panel < 9; ++panel) + { + result.panelSettings[panel].fsrLowThreshold = new UInt16[4]; + result.panelSettings[panel].fsrHighThreshold = new UInt16[4]; + } return result; } diff --git a/smx-config/Widgets.cs b/smx-config/Widgets.cs index 54cd3b6..bba9832 100644 --- a/smx-config/Widgets.cs +++ b/smx-config/Widgets.cs @@ -8,6 +8,7 @@ using System.Windows.Data; using System.Windows.Shapes; using System.Windows.Media.Imaging; using System.ComponentModel; +using System.Collections.Generic; namespace smx_config { @@ -139,44 +140,38 @@ namespace smx_config }); } + Dictionary panelNameToIndex = new Dictionary() { + { "up-left", 0 }, + { "up", 1 }, + { "up-right", 2 }, + { "left", 3 }, + { "center", 4 }, + { "right", 5 }, + { "down-left", 6 }, + { "down", 7 }, + { "down-right", 8 }, + + // The cardinal and corner sliders write to the down and up-right panels, and + // are then synced to the other panels by SyncUnifiedThresholds. + { "cardinal", 7 }, + { "corner", 2 }, + }; private void SetValueToConfig(ref SMX.SMXConfig config) { + int panelIdx = panelNameToIndex[Type]; if(!config.fsr()) { byte lower = (byte) slider.LowerValue; byte upper = (byte) slider.UpperValue; - - switch(Type) - { - case "up-left": config.panelThreshold0Low = lower; config.panelThreshold0High = upper; break; - case "up": config.panelThreshold1Low = lower; config.panelThreshold1High = upper; break; - case "up-right": config.panelThreshold2Low = lower; config.panelThreshold2High = upper; break; - case "left": config.panelThreshold3Low = lower; config.panelThreshold3High = upper; break; - case "center": config.panelThreshold4Low = lower; config.panelThreshold4High = upper; break; - case "right": config.panelThreshold5Low = lower; config.panelThreshold5High = upper; break; - case "down-left": config.panelThreshold6Low = lower; config.panelThreshold6High = upper; break; - case "down": config.panelThreshold7Low = lower; config.panelThreshold7High = upper; break; - case "down-right": config.panelThreshold8Low = lower; config.panelThreshold8High = upper; break; - case "cardinal": config.panelThreshold7Low = lower; config.panelThreshold7High = upper; break; - case "corner": config.panelThreshold2Low = lower; config.panelThreshold2High = upper; break; - } + config.panelSettings[panelIdx].loadCellLowThreshold = lower; + config.panelSettings[panelIdx].loadCellHighThreshold = upper; } else { UInt16 lower = (UInt16) slider.LowerValue; UInt16 upper = (UInt16) slider.UpperValue; - - switch(Type) + for(int sensor = 0; sensor < 4; ++sensor) { - case "up-left": config.individualPanelFSRLow[0] = lower; config.individualPanelFSRHigh[0] = upper; break; - case "up": config.individualPanelFSRLow[1] = lower; config.individualPanelFSRHigh[1] = upper; break; - case "up-right": config.individualPanelFSRLow[2] = lower; config.individualPanelFSRHigh[2] = upper; break; - case "left": config.individualPanelFSRLow[3] = lower; config.individualPanelFSRHigh[3] = upper; break; - case "center": config.individualPanelFSRLow[4] = lower; config.individualPanelFSRHigh[4] = upper; break; - case "right": config.individualPanelFSRLow[5] = lower; config.individualPanelFSRHigh[5] = upper; break; - case "down-left": config.individualPanelFSRLow[6] = lower; config.individualPanelFSRHigh[6] = upper; break; - case "down": config.individualPanelFSRLow[7] = lower; config.individualPanelFSRHigh[7] = upper; break; - case "down-right": config.individualPanelFSRLow[8] = lower; config.individualPanelFSRHigh[8] = upper; break; - case "cardinal": config.individualPanelFSRLow[7] = lower; config.individualPanelFSRHigh[7] = upper; break; - case "corner": config.individualPanelFSRLow[2] = lower; config.individualPanelFSRHigh[2] = upper; break; + config.panelSettings[panelIdx].fsrLowThreshold[sensor] = lower; + config.panelSettings[panelIdx].fsrHighThreshold[sensor] = upper; } } @@ -187,43 +182,15 @@ namespace smx_config private void GetValueFromConfig(SMX.SMXConfig config, out int lower, out int upper) { + int panelIdx = panelNameToIndex[Type]; + if(!config.fsr()) { - switch(Type) - { - case "up-left": lower = config.panelThreshold0Low; upper = config.panelThreshold0High; return; - case "up": lower = config.panelThreshold1Low; upper = config.panelThreshold1High; return; - case "up-right": lower = config.panelThreshold2Low; upper = config.panelThreshold2High; return; - case "left": lower = config.panelThreshold3Low; upper = config.panelThreshold3High; return; - case "center": lower = config.panelThreshold4Low; upper = config.panelThreshold4High; return; - case "right": lower = config.panelThreshold5Low; upper = config.panelThreshold5High; return; - case "down-left": lower = config.panelThreshold6Low; upper = config.panelThreshold6High; return; - case "down": lower = config.panelThreshold7Low; upper = config.panelThreshold7High; return; - case "down-right": lower = config.panelThreshold8Low; upper = config.panelThreshold8High; return; - case "cardinal": lower = config.panelThreshold7Low; upper = config.panelThreshold7High; return; - case "corner": lower = config.panelThreshold2Low; upper = config.panelThreshold2High; return; - default: - lower = upper = 0; - return; - } + lower = config.panelSettings[panelIdx].loadCellLowThreshold; + upper = config.panelSettings[panelIdx].loadCellHighThreshold; } else { - switch(Type) - { - case "up-left": lower = config.individualPanelFSRLow[0]; upper = config.individualPanelFSRHigh[0]; return; - case "up": lower = config.individualPanelFSRLow[1]; upper = config.individualPanelFSRHigh[1]; return; - case "up-right": lower = config.individualPanelFSRLow[2]; upper = config.individualPanelFSRHigh[2]; return; - case "left": lower = config.individualPanelFSRLow[3]; upper = config.individualPanelFSRHigh[3]; return; - case "center": lower = config.individualPanelFSRLow[4]; upper = config.individualPanelFSRHigh[4]; return; - case "right": lower = config.individualPanelFSRLow[5]; upper = config.individualPanelFSRHigh[5]; return; - case "down-left": lower = config.individualPanelFSRLow[6]; upper = config.individualPanelFSRHigh[6]; return; - case "down": lower = config.individualPanelFSRLow[7]; upper = config.individualPanelFSRHigh[7]; return; - case "down-right": lower = config.individualPanelFSRLow[8]; upper = config.individualPanelFSRHigh[8]; return; - case "cardinal": lower = config.individualPanelFSRLow[7]; upper = config.individualPanelFSRHigh[7]; return; - case "corner": lower = config.individualPanelFSRLow[2]; upper = config.individualPanelFSRHigh[2]; return; - default: - lower = upper = 0; - return; - } + lower = config.panelSettings[panelIdx].fsrLowThreshold[0]; + upper = config.panelSettings[panelIdx].fsrHighThreshold[0]; } } @@ -382,8 +349,6 @@ namespace smx_config SMX.SMXConfig config = activePad.Item2; ConfigPresets.SetPreset(Type, ref config); - Console.WriteLine("PresetButton::Select (" + Type + "): " + - config.panelThreshold1Low + ", " + config.panelThreshold4Low + ", " + config.panelThreshold7Low + ", " + config.panelThreshold2Low); SMX.SMX.SetConfig(pad, config); } CurrentSMXDevice.singleton.FireConfigurationChanged(this);