Implement the V5 config packet.

master
Glenn Maynard 6 years ago
parent 0184e46022
commit 6cc979b1dc
  1. 129
      sdk/SMX.h
  2. 30
      sdk/Windows/SMXDevice.cpp
  3. 147
      smx-config/ConfigPresets.cs
  4. 44
      smx-config/Helpers.cs
  5. 165
      smx-config/SMX.cs
  6. 93
      smx-config/Widgets.cs

@ -2,6 +2,7 @@
#define SMX_H #define SMX_H
#include <stdint.h> #include <stdint.h>
#include <stddef.h> // for offsetof
#ifdef SMX_EXPORTS #ifdef SMX_EXPORTS
#define SMX_API extern "C" __declspec(dllexport) #define SMX_API extern "C" __declspec(dllexport)
@ -151,19 +152,54 @@ enum SMXConfigFlags {
// If set, panels are using FSRs, otherwise load cells. // If set, panels are using FSRs, otherwise load cells.
PlatformFlags_FSR = 1 << 1, 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 // The configuration for a connected controller. This can be retrieved with SMX_GetConfig
// and modified with SMX_SetConfig. // and modified with SMX_SetConfig.
// //
// The order and packing of this struct corresponds to the configuration packet sent to // The order and packing of this struct corresponds to the configuration packet sent to
// the master controller, so it must not be changed. // the master controller, so it must not be changed.
#pragma pack(push, 1)
struct SMXConfig struct SMXConfig
{ {
// These fields are unused and must be left at their existing values. // The firmware version of the master controller. Where supported (version 2 and up), this
uint8_t unused1 = 0xFF, unused2 = 0xFF; // will always read back the firmware version. This will default to 0xFF on version 1, and
uint8_t unused3 = 0xFF, unused4 = 0xFF; // we'll always write 0xFF here so it doesn't change on that firmware version.
uint8_t unused5 = 0xFF, unused6 = 0xFF; //
// 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. // 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 // 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. // later firmware versions, each panel is configured independently.
// //
// Setting a value to 0xFF disables that threshold. // 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. // These are internal tunables and should be left unchanged.
uint16_t debounceNodelayMilliseconds = 0;
uint16_t debounceDelayMs = 0;
uint16_t panelDebounceMicroseconds = 4000; uint16_t panelDebounceMicroseconds = 4000;
uint16_t autoCalibrationPeriodMilliseconds = 1000;
uint8_t autoCalibrationMaxDeviation = 100; uint8_t autoCalibrationMaxDeviation = 100;
uint8_t badSensorMinimumDelaySeconds = 15; uint8_t badSensorMinimumDelaySeconds = 15;
uint16_t autoCalibrationAveragesPerUpdate = 60; uint16_t autoCalibrationAveragesPerUpdate = 60;
uint16_t autoCalibrationSamplesPerAverage = 500;
uint8_t unused7 = 0xFF, unused8 = 0xFF; // The maximum tare value to calibrate to (except on startup).
uint16_t autoCalibrationMaxTare;
uint8_t panelThreshold1Low = 0xFF, panelThreshold1High = 0xFF; // was "up"
// Which sensors on each panel to enable. This can be used to disable sensors that // 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: // we know aren't populated. This is packed, with four sensors on two pads per byte:
@ -202,76 +235,34 @@ struct SMXConfig
// range. // range.
uint8_t stepColor[3*9]; 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. // The default color to set the platform LED strip to.
uint8_t platformStripColor[3]; 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. // 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 // 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 // the master controller's built-in auto lighting and not lights data send
// from the SDK. // from the SDK.
uint16_t autoLightPanelMask; 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 // 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 // as we add fields, so the ABI doesn't change. Applications should leave
// any data in here unchanged when calling SMX_SetConfig. // any data in here unchanged when calling SMX_SetConfig.
uint8_t padding[120]; uint8_t padding[13];
}; };
#pragma pack(pop) #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"); static_assert(sizeof(SMXConfig) == 250, "Expected 250 bytes");
// The values (except for Off) correspond with the protocol and must not be changed. // The values (except for Off) correspond with the protocol and must not be changed.

@ -200,7 +200,11 @@ void SMX::SMXDevice::FactoryReset()
// Send a factory reset command, and then read the new configuration. // Send a factory reset command, and then read the new configuration.
LockMutex Lock(m_Lock); LockMutex Lock(m_Lock);
SendCommandLocked("f\n"); 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. // We now have the new configuration.
m_Lock.AssertLockedByCurrentThread(); m_Lock.AssertLockedByCurrentThread();
CallUpdateCallback(SMXUpdateCallback_FactoryResetCommandComplete); CallUpdateCallback(SMXUpdateCallback_FactoryResetCommandComplete);
@ -260,7 +264,10 @@ void SMX::SMXDevice::HandlePackets()
HandleSensorTestDataResponse(buf); HandleSensorTestDataResponse(buf);
break; 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':
case 'G':
{ {
// This command reads back the configuration we wrote with 'w', or the defaults if // This command reads back the configuration we wrote with 'w', or the defaults if
// we haven't written any. // we haven't written any.
@ -310,8 +317,11 @@ void SMX::SMXDevice::SendConfig()
if(m_bWaitingForConfigResponse) if(m_bWaitingForConfigResponse)
return; return;
// Write configuration command: SMXDeviceInfo deviceInfo = m_pConnection->GetDeviceInfo();
string sData = ssprintf("w");
// 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); int8_t iSize = sizeof(SMXConfig);
// Firmware through version 3 allowed config packets up to 128 bytes. Additions // Firmware through version 3 allowed config packets up to 128 bytes. Additions
@ -344,8 +354,10 @@ void SMX::SMXDevice::SendConfig()
m_bWaitingForConfigResponse = true; m_bWaitingForConfigResponse = true;
// After we write the configuration, read back the updated configuration to // After we write the configuration, read back the updated configuration to
// verify it. // verify it. This command is "g" in versions 1-4, and "G" in versions 5 and
SendCommandLocked("g\n", [this](string response) { // newer.
SendCommandLocked(
deviceInfo.m_iFirmwareVersion >= 5? "G":"g\n", [this](string response) {
m_bWaitingForConfigResponse = false; m_bWaitingForConfigResponse = false;
}); });
} }
@ -387,9 +399,11 @@ void SMX::SMXDevice::CheckActive()
m_pConnection->SetActive(true); m_pConnection->SetActive(true);
// Read the current configuration. The device will return a "g" response containing SMXDeviceInfo deviceInfo = m_pConnection->GetDeviceInfo();
// its current SMXConfig.
SendCommandLocked("g\n"); // 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. // Check if we need to request test mode data.

@ -34,34 +34,19 @@ namespace smx_config
// Return true if the config matches, only comparing values that we set in presets. // Return true if the config matches, only comparing values that we set in presets.
static private bool SamePreset(SMX.SMXConfig config1, SMX.SMXConfig config2) static private bool SamePreset(SMX.SMXConfig config1, SMX.SMXConfig config2)
{ {
// These aren't arrays for compatibility reasons. for(int panel = 0; panel < 9; ++panel)
if( config1.panelThreshold0High != config2.panelThreshold0High || {
config1.panelThreshold1High != config2.panelThreshold1High || if(config1.panelSettings[panel].loadCellLowThreshold != config2.panelSettings[panel].loadCellLowThreshold ||
config1.panelThreshold2High != config2.panelThreshold2High || config1.panelSettings[panel].loadCellHighThreshold != config2.panelSettings[panel].loadCellHighThreshold)
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; return false;
for(int i = 0; i < 9; ++i) for(int sensor = 0; sensor < 4; ++sensor)
{ {
if(config1.individualPanelFSRLow[i] != config2.individualPanelFSRLow[i] || if(config1.panelSettings[panel].fsrLowThreshold[sensor] != config2.panelSettings[panel].fsrLowThreshold[sensor] ||
config1.individualPanelFSRHigh[i] != config2.individualPanelFSRHigh[i]) config1.panelSettings[panel].fsrHighThreshold[sensor] != config2.panelSettings[panel].fsrHighThreshold[sensor])
return false; return false;
} }
}
return true; return true;
} }
@ -76,79 +61,56 @@ namespace smx_config
} }
} }
static private void SetHighPreset(ref SMX.SMXConfig 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)
{ {
config.panelThreshold7Low = // cardinal for(int panel = 0; panel < 9; ++panel)
config.panelThreshold1Low = // up {
config.panelThreshold2Low = 20; // corner config.panelSettings[panel].loadCellLowThreshold = loadCellLow;
config.panelThreshold7High = // cardinal config.panelSettings[panel].loadCellHighThreshold = loadCellHigh;
config.panelThreshold1High = // up }
config.panelThreshold2High = 25; // corner
config.panelThreshold4Low = 20; // center // Center:
config.panelThreshold4High = 30; config.panelSettings[4].loadCellLowThreshold = loadCellLowCenter;
config.panelSettings[4].loadCellHighThreshold = loadCellHighCenter;
config.individualPanelFSRLow[7] = // cardinal for(int panel = 0; panel < 9; ++panel)
config.individualPanelFSRLow[1] = // up {
config.individualPanelFSRLow[2] = 275; // corner for(int sensor = 0; sensor < 4; ++sensor)
config.individualPanelFSRHigh[7] = // cardinal {
config.individualPanelFSRHigh[1] = // up config.panelSettings[panel].fsrLowThreshold[sensor] = fsrLow;
config.individualPanelFSRHigh[2] = 300; // corner config.panelSettings[panel].fsrHighThreshold[sensor] = fsrHigh;
}
}
config.individualPanelFSRLow[4] = 400; // center // Center:
config.individualPanelFSRHigh[4] = 500; for(int sensor = 0; sensor < 4; ++sensor)
{
config.panelSettings[4].fsrLowThreshold[sensor] = fsrLowCenter;
config.panelSettings[4].fsrHighThreshold[sensor] = fsrHighCenter;
}
}
SyncUnifiedThresholds(ref config); static private void SetHighPreset(ref SMX.SMXConfig config)
{
SetPreset(ref config,
20, 25, 20, 30,
275, 300, 275, 300);
} }
static private void SetNormalPreset(ref SMX.SMXConfig config) static private void SetNormalPreset(ref SMX.SMXConfig config)
{ {
config.panelThreshold7Low = // cardinal SetPreset(ref config,
config.panelThreshold1Low = // up 33, 42, 35, 60,
config.panelThreshold2Low = 33; // corner 650, 700, 845, 895);
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);
} }
static private void SetLowPreset(ref SMX.SMXConfig config) static private void SetLowPreset(ref SMX.SMXConfig config)
{ {
config.panelThreshold7Low = // cardinal SetPreset(ref config,
config.panelThreshold1Low = // up 70, 80, 100, 120,
config.panelThreshold2Low = 70; // corner 800, 875, 845, 895);
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);
} }
// The simplified configuration scheme sets thresholds for up, center, cardinal directions // 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) static public void SyncUnifiedThresholds(ref SMX.SMXConfig config)
{ {
// left = right = down (cardinal) // left = right = down (cardinal)
config.panelThreshold3Low = config.panelThreshold5Low = config.panelThreshold7Low; config.panelSettings[3].loadCellLowThreshold = config.panelSettings[5].loadCellLowThreshold = config.panelSettings[7].loadCellLowThreshold;
config.panelThreshold3High = config.panelThreshold5High = config.panelThreshold7High; config.panelSettings[3].loadCellHighThreshold = config.panelSettings[5].loadCellHighThreshold = config.panelSettings[7].loadCellHighThreshold;
// UL = DL = DR = UR (corners) // UL = DL = DR = UR (corners)
config.panelThreshold0Low = config.panelThreshold6Low = config.panelThreshold8Low = config.panelThreshold2Low; config.panelSettings[0].loadCellLowThreshold = config.panelSettings[6].loadCellLowThreshold = config.panelSettings[8].loadCellLowThreshold = config.panelSettings[2].loadCellLowThreshold;
config.panelThreshold0High = config.panelThreshold6High = config.panelThreshold8High = config.panelThreshold2High; config.panelSettings[0].loadCellHighThreshold = config.panelSettings[6].loadCellHighThreshold = config.panelSettings[8].loadCellHighThreshold = config.panelSettings[2].loadCellHighThreshold;
// Do the same for FSR thresholds. // Do the same for FSR thresholds.
config.individualPanelFSRLow[3] = config.individualPanelFSRLow[5] = config.individualPanelFSRLow[7]; for(int sensor = 0; sensor < 4; ++sensor)
config.individualPanelFSRHigh[3] = config.individualPanelFSRHigh[5] = config.individualPanelFSRHigh[7]; {
config.individualPanelFSRLow[0] = config.individualPanelFSRLow[6] = config.individualPanelFSRLow[8] = config.individualPanelFSRLow[2]; config.panelSettings[3].fsrLowThreshold[sensor] = config.panelSettings[5].fsrLowThreshold[sensor] = config.panelSettings[7].fsrLowThreshold[sensor];
config.individualPanelFSRHigh[0] = config.individualPanelFSRHigh[6] = config.individualPanelFSRHigh[8] = config.individualPanelFSRHigh[2]; 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 // Return true if the panel thresholds are already synced, so SyncUnifiedThresholds would

@ -528,27 +528,13 @@ namespace smx_config
{ {
Dictionary<string, Object> dict = new Dictionary<string, Object>(); Dictionary<string, Object> dict = new Dictionary<string, Object>();
List<int> panelLowThresholds = new List<int>(); List<int> panelLowThresholds = new List<int>();
panelLowThresholds.Add(config.panelThreshold0Low); for(int panel = 0; panel < 9; ++panel)
panelLowThresholds.Add(config.panelThreshold1Low); panelLowThresholds.Add(config.panelSettings[panel].loadCellLowThreshold);
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);
dict.Add("panelLowThresholds", panelLowThresholds); dict.Add("panelLowThresholds", panelLowThresholds);
List<int> panelHighThresholds = new List<int>(); List<int> panelHighThresholds = new List<int>();
panelHighThresholds.Add(config.panelThreshold0High); for(int panel = 0; panel < 9; ++panel)
panelHighThresholds.Add(config.panelThreshold1High); panelLowThresholds.Add(config.panelSettings[panel].loadCellHighThreshold);
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);
dict.Add("panelHighThresholds", panelHighThresholds); dict.Add("panelHighThresholds", panelHighThresholds);
// Store the enabled panel mask as a simple list of which panels are selected. // 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. // Read the thresholds. If any values are missing, we'll leave the value in config alone.
List<Object> newPanelLowThresholds = dict.Get("panelLowThresholds", new List<Object>()); List<Object> newPanelLowThresholds = dict.Get("panelLowThresholds", new List<Object>());
config.panelThreshold0Low = newPanelLowThresholds.Get(0, config.panelThreshold0Low); for(int panel = 0; panel < 9; ++panel)
config.panelThreshold1Low = newPanelLowThresholds.Get(1, config.panelThreshold1Low); config.panelSettings[panel].loadCellLowThreshold = newPanelLowThresholds.Get(panel, config.panelSettings[panel].loadCellLowThreshold);
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);
List<Object> newPanelHighThresholds = dict.Get("panelHighThresholds", new List<Object>()); List<Object> newPanelHighThresholds = dict.Get("panelHighThresholds", new List<Object>());
config.panelThreshold0High = newPanelHighThresholds.Get(0, config.panelThreshold0High); for(int panel = 0; panel < 9; ++panel)
config.panelThreshold1High = newPanelHighThresholds.Get(1, config.panelThreshold1High); config.panelSettings[panel].loadCellHighThreshold = newPanelHighThresholds.Get(panel, config.panelSettings[panel].loadCellHighThreshold);
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);
List<Object> enabledPanelList = dict.Get<List<Object>>("enabledPanels", null); List<Object> enabledPanelList = dict.Get<List<Object>>("enabledPanels", null);
if(enabledPanelList != null) if(enabledPanelList != null)

@ -27,22 +27,38 @@ namespace SMX
PlatformFlags_FSR = 1 << 1, 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)] [StructLayout(LayoutKind.Sequential, Pack=1)]
public struct SMXConfig { public struct SMXConfig {
public Byte unused1, unused2; public Byte masterVersion;
public Byte unused3, unused4; public Byte configVersion;
public Byte unused5, unused6;
public UInt16 masterDebounceMilliseconds; // Packed flags (SMXConfigFlags).
public Byte panelThreshold7Low, panelThreshold7High; // was "cardinal" public Byte flags;
public Byte panelThreshold4Low, panelThreshold4High; // was "center"
public Byte panelThreshold2Low, panelThreshold2High; // was "corner" public UInt16 debounceNodelayMilliseconds;
public UInt16 debounceDelayMs;
public UInt16 panelDebounceMicroseconds; public UInt16 panelDebounceMicroseconds;
public UInt16 autoCalibrationPeriodMilliseconds;
public Byte autoCalibrationMaxDeviation; public Byte autoCalibrationMaxDeviation;
public Byte badSensorMinimumDelaySeconds; public Byte badSensorMinimumDelaySeconds;
public UInt16 autoCalibrationAveragesPerUpdate; public UInt16 autoCalibrationAveragesPerUpdate;
public Byte unused7, unused8; public UInt16 autoCalibrationSamplesPerAverage;
public Byte panelThreshold1Low, panelThreshold1High; // was "up" public UInt16 autoCalibrationMaxTare;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public Byte[] enabledSensors; public Byte[] enabledSensors;
@ -52,27 +68,28 @@ namespace SMX
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3*9)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3*9)]
public Byte[] stepColor; 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 Byte panelRotation;
public UInt16 autoCalibrationSamplesPerAverage;
public Byte masterVersion;
public Byte configVersion;
// The remaining thresholds (configVersion >= 2). // Per-panel sensor settings:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public Byte[] unused9; public PackedSensorSettings[] panelSettings;
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;
// Packed flags (SMXConfigFlags). // These are internal tunables and should be left unchanged.
public Byte flags; 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 // It would be simpler to set flags to [MarshalAs(UnmanagedType.U8)], but
// that doesn't work. // that doesn't work.
@ -92,30 +109,6 @@ namespace SMX
return masterVersion >= 4 && (configFlags & SMXConfigFlags.PlatformFlags_FSR) != 0; 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 // enabledSensors is a mask of which panels are enabled. Return this as an array
// for convenience. // for convenience.
public bool[] GetEnabledPanels() public bool[] GetEnabledPanels()
@ -167,60 +160,47 @@ namespace SMX
// The layout of this structure (and the underlying C struct) matches the firmware configuration // 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 // data. This is a bit inconvenient for the panel thresholds which aren't contiguous, so these
// helpers just convert them to and from arrays. // helpers just convert them to and from arrays.
// XXX: used?
public Byte[] GetLowThresholds() public Byte[] GetLowThresholds()
{ {
return new Byte[] { return new Byte[] {
panelThreshold0Low, panelSettings[0].loadCellLowThreshold,
panelThreshold1Low, panelSettings[1].loadCellLowThreshold,
panelThreshold2Low, panelSettings[2].loadCellLowThreshold,
panelThreshold3Low, panelSettings[3].loadCellLowThreshold,
panelThreshold4Low, panelSettings[4].loadCellLowThreshold,
panelThreshold5Low, panelSettings[5].loadCellLowThreshold,
panelThreshold6Low, panelSettings[6].loadCellLowThreshold,
panelThreshold7Low, panelSettings[7].loadCellLowThreshold,
panelThreshold8Low, panelSettings[8].loadCellLowThreshold,
}; };
} }
public Byte[] GetHighThresholds() public Byte[] GetHighThresholds()
{ {
return new Byte[] { return new Byte[] {
panelThreshold0High, panelSettings[0].loadCellHighThreshold,
panelThreshold1High, panelSettings[1].loadCellHighThreshold,
panelThreshold2High, panelSettings[2].loadCellHighThreshold,
panelThreshold3High, panelSettings[3].loadCellHighThreshold,
panelThreshold4High, panelSettings[4].loadCellHighThreshold,
panelThreshold5High, panelSettings[5].loadCellHighThreshold,
panelThreshold6High, panelSettings[6].loadCellHighThreshold,
panelThreshold7High, panelSettings[7].loadCellHighThreshold,
panelThreshold8High, panelSettings[8].loadCellHighThreshold,
}; };
} }
public void SetLowThresholds(Byte[] values) public void SetLowThresholds(Byte[] values)
{ {
panelThreshold0Low = values[0]; for(int panel = 0; panel < 9; ++panel)
panelThreshold1Low = values[1]; panelSettings[panel].loadCellLowThreshold = values[panel];
panelThreshold2Low = values[2];
panelThreshold3Low = values[3];
panelThreshold4Low = values[4];
panelThreshold5Low = values[5];
panelThreshold6Low = values[6];
panelThreshold7Low = values[7];
panelThreshold8Low = values[8];
} }
public void SetHighThresholds(Byte[] values) public void SetHighThresholds(Byte[] values)
{ {
panelThreshold0High = values[0]; for(int panel = 0; panel < 9; ++panel)
panelThreshold1High = values[1]; panelSettings[panel].loadCellHighThreshold = values[panel];
panelThreshold2High = values[2];
panelThreshold3High = values[3];
panelThreshold4High = values[4];
panelThreshold5High = values[5];
panelThreshold6High = values[6];
panelThreshold7High = values[7];
panelThreshold8High = values[8];
} }
// Create an empty SMXConfig. // Create an empty SMXConfig.
@ -229,10 +209,13 @@ namespace SMX
SMXConfig result = new SMXConfig(); SMXConfig result = new SMXConfig();
result.enabledSensors = new Byte[5]; result.enabledSensors = new Byte[5];
result.stepColor = new Byte[3*9]; result.stepColor = new Byte[3*9];
result.individualPanelFSRLow = new UInt16[9]; result.panelSettings = new PackedSensorSettings[9];
result.individualPanelFSRHigh = new UInt16[9];
result.platformStripColor = new Byte[3]; 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; return result;
} }

@ -8,6 +8,7 @@ using System.Windows.Data;
using System.Windows.Shapes; using System.Windows.Shapes;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.ComponentModel; using System.ComponentModel;
using System.Collections.Generic;
namespace smx_config namespace smx_config
{ {
@ -139,44 +140,38 @@ namespace smx_config
}); });
} }
Dictionary<string, int> panelNameToIndex = new Dictionary<string, int>() {
{ "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) private void SetValueToConfig(ref SMX.SMXConfig config)
{ {
int panelIdx = panelNameToIndex[Type];
if(!config.fsr()) if(!config.fsr())
{ {
byte lower = (byte) slider.LowerValue; byte lower = (byte) slider.LowerValue;
byte upper = (byte) slider.UpperValue; byte upper = (byte) slider.UpperValue;
config.panelSettings[panelIdx].loadCellLowThreshold = lower;
switch(Type) config.panelSettings[panelIdx].loadCellHighThreshold = upper;
{
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;
}
} else { } else {
UInt16 lower = (UInt16) slider.LowerValue; UInt16 lower = (UInt16) slider.LowerValue;
UInt16 upper = (UInt16) slider.UpperValue; UInt16 upper = (UInt16) slider.UpperValue;
for(int sensor = 0; sensor < 4; ++sensor)
switch(Type)
{ {
case "up-left": config.individualPanelFSRLow[0] = lower; config.individualPanelFSRHigh[0] = upper; break; config.panelSettings[panelIdx].fsrLowThreshold[sensor] = lower;
case "up": config.individualPanelFSRLow[1] = lower; config.individualPanelFSRHigh[1] = upper; break; config.panelSettings[panelIdx].fsrHighThreshold[sensor] = upper;
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;
} }
} }
@ -187,43 +182,15 @@ namespace smx_config
private void GetValueFromConfig(SMX.SMXConfig config, out int lower, out int upper) private void GetValueFromConfig(SMX.SMXConfig config, out int lower, out int upper)
{ {
int panelIdx = panelNameToIndex[Type];
if(!config.fsr()) if(!config.fsr())
{ {
switch(Type) lower = config.panelSettings[panelIdx].loadCellLowThreshold;
{ upper = config.panelSettings[panelIdx].loadCellHighThreshold;
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;
}
} else { } else {
switch(Type) lower = config.panelSettings[panelIdx].fsrLowThreshold[0];
{ upper = config.panelSettings[panelIdx].fsrHighThreshold[0];
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;
}
} }
} }
@ -382,8 +349,6 @@ namespace smx_config
SMX.SMXConfig config = activePad.Item2; SMX.SMXConfig config = activePad.Item2;
ConfigPresets.SetPreset(Type, ref config); ConfigPresets.SetPreset(Type, ref config);
Console.WriteLine("PresetButton::Select (" + Type + "): " +
config.panelThreshold1Low + ", " + config.panelThreshold4Low + ", " + config.panelThreshold7Low + ", " + config.panelThreshold2Low);
SMX.SMX.SetConfig(pad, config); SMX.SMX.SetConfig(pad, config);
} }
CurrentSMXDevice.singleton.FireConfigurationChanged(this); CurrentSMXDevice.singleton.FireConfigurationChanged(this);

Loading…
Cancel
Save