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. 159
      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
#include <stdint.h>
#include <stddef.h> // 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.

@ -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.

@ -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

@ -528,27 +528,13 @@ namespace smx_config
{
Dictionary<string, Object> dict = new Dictionary<string, Object>();
List<int> panelLowThresholds = new List<int>();
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<int> panelHighThresholds = new List<int>();
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<Object> newPanelLowThresholds = dict.Get("panelLowThresholds", new List<Object>());
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<Object> newPanelHighThresholds = dict.Get("panelHighThresholds", new List<Object>());
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<Object> enabledPanelList = dict.Get<List<Object>>("enabledPanels", null);
if(enabledPanelList != null)

@ -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;
}

@ -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<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)
{
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);

Loading…
Cancel
Save