diff --git a/sdk/Windows/SMX.vcxproj b/sdk/Windows/SMX.vcxproj index 2052e98..f6d82ad 100644 --- a/sdk/Windows/SMX.vcxproj +++ b/sdk/Windows/SMX.vcxproj @@ -14,6 +14,7 @@ + @@ -28,6 +29,7 @@ + @@ -146,4 +148,4 @@ - + \ No newline at end of file diff --git a/sdk/Windows/SMX.vcxproj.filters b/sdk/Windows/SMX.vcxproj.filters index 4ca4d76..b220650 100644 --- a/sdk/Windows/SMX.vcxproj.filters +++ b/sdk/Windows/SMX.vcxproj.filters @@ -46,6 +46,9 @@ Source Files + + Source Files + @@ -84,5 +87,8 @@ Source Files + + Source Files + - + \ No newline at end of file diff --git a/sdk/Windows/SMXConfigPacket.cpp b/sdk/Windows/SMXConfigPacket.cpp new file mode 100644 index 0000000..fe53b85 --- /dev/null +++ b/sdk/Windows/SMXConfigPacket.cpp @@ -0,0 +1,169 @@ +#include "SMXConfigPacket.h" +#include +#include + +// The config packet format changed in version 5. This handles compatibility with +// the old configuration packet. The config packet in SMX.h matches the new format. +// + + +#pragma pack(push, 1) +struct OldSMXConfig +{ + uint8_t unused1 = 0xFF, unused2 = 0xFF; + uint8_t unused3 = 0xFF, unused4 = 0xFF; + uint8_t unused5 = 0xFF, unused6 = 0xFF; + + 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" + + uint16_t panelDebounceMicroseconds = 4000; + uint16_t autoCalibrationPeriodMilliseconds = 1000; + uint8_t autoCalibrationMaxDeviation = 100; + uint8_t badSensorMinimumDelaySeconds = 15; + uint16_t autoCalibrationAveragesPerUpdate = 60; + + uint8_t unused7 = 0xFF, unused8 = 0xFF; + + uint8_t panelThreshold1Low = 0xFF, panelThreshold1High = 0xFF; // was "up" + + uint8_t enabledSensors[5]; + + uint8_t autoLightsTimeout = 1000/128; // 1 second + + uint8_t stepColor[3*9]; + + uint8_t panelRotation; + + uint16_t autoCalibrationSamplesPerAverage = 500; + + uint8_t masterVersion = 0xFF; + uint8_t configVersion = 0x03; + + uint8_t unused9[10]; + uint8_t panelThreshold0Low, panelThreshold0High; + uint8_t panelThreshold3Low, panelThreshold3High; + uint8_t panelThreshold5Low, panelThreshold5High; + uint8_t panelThreshold6Low, panelThreshold6High; + uint8_t panelThreshold8Low, panelThreshold8High; + + uint16_t debounceDelayMilliseconds = 0; + + uint8_t padding[164]; +}; +#pragma pack(pop) +static_assert(offsetof(OldSMXConfig, padding) == 86, "Incorrect padding alignment"); +static_assert(sizeof(OldSMXConfig) == 250, "Expected 250 bytes"); + +void ConvertToNewConfig(const vector &oldConfigData, SMXConfig &newConfig) +{ + // Copy data in its order within OldSMXConfig. This lets us easily stop at each + // known packet version. Any fields that aren't present in oldConfigData will be + // left at their default values in SMXConfig. + const OldSMXConfig &oldConfig = (OldSMXConfig &) *oldConfigData.data(); + + newConfig.debounceNodelayMilliseconds = oldConfig.masterDebounceMilliseconds; + + newConfig.panelSettings[7].loadCellLowThreshold = oldConfig.panelThreshold7Low; + newConfig.panelSettings[4].loadCellLowThreshold = oldConfig.panelThreshold4Low; + newConfig.panelSettings[2].loadCellLowThreshold = oldConfig.panelThreshold2Low; + + newConfig.panelSettings[7].loadCellHighThreshold = oldConfig.panelThreshold7High; + newConfig.panelSettings[4].loadCellHighThreshold = oldConfig.panelThreshold4High; + newConfig.panelSettings[2].loadCellHighThreshold = oldConfig.panelThreshold2High; + + newConfig.panelDebounceMicroseconds = oldConfig.panelDebounceMicroseconds; + newConfig.autoCalibrationMaxDeviation = oldConfig.autoCalibrationMaxDeviation; + newConfig.badSensorMinimumDelaySeconds = oldConfig.badSensorMinimumDelaySeconds; + newConfig.autoCalibrationAveragesPerUpdate = oldConfig.autoCalibrationAveragesPerUpdate; + + newConfig.panelSettings[1].loadCellLowThreshold = oldConfig.panelThreshold1Low; + newConfig.panelSettings[1].loadCellHighThreshold = oldConfig.panelThreshold1High; + + memcpy(newConfig.enabledSensors, oldConfig.enabledSensors, sizeof(newConfig.enabledSensors)); + newConfig.autoLightsTimeout = oldConfig.autoLightsTimeout; + memcpy(newConfig.stepColor, oldConfig.stepColor, sizeof(newConfig.stepColor)); + newConfig.panelRotation = oldConfig.panelRotation; + newConfig.autoCalibrationSamplesPerAverage = oldConfig.autoCalibrationSamplesPerAverage; + + if(oldConfig.configVersion == 0xFF) + return; + + newConfig.masterVersion = oldConfig.masterVersion; + newConfig.configVersion = oldConfig.configVersion; + + if(oldConfig.configVersion < 2) + return; + + newConfig.panelSettings[0].loadCellLowThreshold = oldConfig.panelThreshold0Low; + newConfig.panelSettings[3].loadCellLowThreshold = oldConfig.panelThreshold3Low; + newConfig.panelSettings[5].loadCellLowThreshold = oldConfig.panelThreshold5Low; + newConfig.panelSettings[6].loadCellLowThreshold = oldConfig.panelThreshold6Low; + newConfig.panelSettings[8].loadCellLowThreshold = oldConfig.panelThreshold8Low; + + newConfig.panelSettings[0].loadCellHighThreshold = oldConfig.panelThreshold0High; + newConfig.panelSettings[3].loadCellHighThreshold = oldConfig.panelThreshold3High; + newConfig.panelSettings[5].loadCellHighThreshold = oldConfig.panelThreshold5High; + newConfig.panelSettings[6].loadCellHighThreshold = oldConfig.panelThreshold6High; + newConfig.panelSettings[8].loadCellHighThreshold = oldConfig.panelThreshold8High; + + if(oldConfig.configVersion < 3) + return; + + newConfig.debounceDelayMilliseconds = oldConfig.debounceDelayMilliseconds; +} + +// oldConfigData contains the data we're replacing. Any fields that exist in the old +// config format and not the new one will be left unchanged. +void ConvertToOldConfig(const SMXConfig &newConfig, vector &oldConfigData) +{ + OldSMXConfig &oldConfig = (OldSMXConfig &) *oldConfigData.data(); + + // We don't need to check configVersion here. It's safe to set all fields in + // the output config packet. If oldConfigData isn't 128 bytes, extend it. + if(oldConfigData.size() < 128) + oldConfigData.resize(128, 0xFF); + + oldConfig.masterDebounceMilliseconds = newConfig.debounceNodelayMilliseconds; + + oldConfig.panelThreshold7Low = newConfig.panelSettings[7].loadCellLowThreshold; + oldConfig.panelThreshold4Low = newConfig.panelSettings[4].loadCellLowThreshold; + oldConfig.panelThreshold2Low = newConfig.panelSettings[2].loadCellLowThreshold; + + oldConfig.panelThreshold7High = newConfig.panelSettings[7].loadCellHighThreshold; + oldConfig.panelThreshold4High = newConfig.panelSettings[4].loadCellHighThreshold; + oldConfig.panelThreshold2High = newConfig.panelSettings[2].loadCellHighThreshold; + + oldConfig.panelDebounceMicroseconds = newConfig.panelDebounceMicroseconds; + oldConfig.autoCalibrationMaxDeviation = newConfig.autoCalibrationMaxDeviation; + oldConfig.badSensorMinimumDelaySeconds = newConfig.badSensorMinimumDelaySeconds; + oldConfig.autoCalibrationAveragesPerUpdate = newConfig.autoCalibrationAveragesPerUpdate; + + oldConfig.panelThreshold1Low = newConfig.panelSettings[1].loadCellLowThreshold; + oldConfig.panelThreshold1High = newConfig.panelSettings[1].loadCellHighThreshold; + + memcpy(oldConfig.enabledSensors, newConfig.enabledSensors, sizeof(newConfig.enabledSensors)); + oldConfig.autoLightsTimeout = newConfig.autoLightsTimeout; + memcpy(oldConfig.stepColor, newConfig.stepColor, sizeof(newConfig.stepColor)); + oldConfig.panelRotation = newConfig.panelRotation; + oldConfig.autoCalibrationSamplesPerAverage = newConfig.autoCalibrationSamplesPerAverage; + + oldConfig.masterVersion = newConfig.masterVersion; + oldConfig.configVersion= newConfig.configVersion; + + oldConfig.panelThreshold0Low = newConfig.panelSettings[0].loadCellLowThreshold; + oldConfig.panelThreshold3Low = newConfig.panelSettings[3].loadCellLowThreshold; + oldConfig.panelThreshold5Low = newConfig.panelSettings[5].loadCellLowThreshold; + oldConfig.panelThreshold6Low = newConfig.panelSettings[6].loadCellLowThreshold; + oldConfig.panelThreshold8Low = newConfig.panelSettings[8].loadCellLowThreshold; + + oldConfig.panelThreshold0High = newConfig.panelSettings[0].loadCellHighThreshold; + oldConfig.panelThreshold3High = newConfig.panelSettings[3].loadCellHighThreshold; + oldConfig.panelThreshold5High = newConfig.panelSettings[5].loadCellHighThreshold; + oldConfig.panelThreshold6High = newConfig.panelSettings[6].loadCellHighThreshold; + oldConfig.panelThreshold8High = newConfig.panelSettings[8].loadCellHighThreshold; + + oldConfig.debounceDelayMilliseconds = newConfig.debounceDelayMilliseconds; +} diff --git a/sdk/Windows/SMXConfigPacket.h b/sdk/Windows/SMXConfigPacket.h new file mode 100644 index 0000000..563a9af --- /dev/null +++ b/sdk/Windows/SMXConfigPacket.h @@ -0,0 +1,12 @@ +#ifndef SMXConfigPacket_h +#define SMXConfigPacket_h + +#include +using namespace std; + +#include "../SMX.h" + +void ConvertToNewConfig(const vector &oldConfig, SMXConfig &newConfig); +void ConvertToOldConfig(const SMXConfig &newConfig, vector &oldConfigData); + +#endif diff --git a/sdk/Windows/SMXDevice.cpp b/sdk/Windows/SMXDevice.cpp index 2304559..1c904ae 100644 --- a/sdk/Windows/SMXDevice.cpp +++ b/sdk/Windows/SMXDevice.cpp @@ -4,6 +4,7 @@ #include "Helpers.h" #include "SMXDeviceConnection.h" #include "SMXDeviceSearch.h" +#include "SMXConfigPacket.h" #include #include #include @@ -283,12 +284,28 @@ void SMX::SMXDevice::HandlePackets() continue; } - // Copy in the configuration. - // Log(ssprintf("Read back configuration: %i bytes, first byte %i", iSize, buf[2])); - memcpy(&config, buf.data()+2, min(iSize, sizeof(config))); + // Store the raw config data in rawConfig. For V1-4 firmwares, this is the + // old config format. + rawConfig.resize(iSize); + memcpy(rawConfig.data(), buf.data()+2, min(iSize, sizeof(config))); + + if(buf[0] == 'g') + { + // Convert the old config format to the new one, so the rest of the SDK and + // user code doesn't need to deal with multiple formats. + ConvertToNewConfig(rawConfig, config); + } + else + { + // This is the new config format. Copy it directly into config. + memcpy(&config, buf.data()+2, min(iSize, sizeof(config))); + } + m_bHaveConfig = true; buf.erase(buf.begin(), buf.begin()+iSize+2); + // Log(ssprintf("Read back configuration: %i bytes, first byte %i", iSize, buf[2])); + CallUpdateCallback(SMXUpdateCallback_Updated); break; } @@ -322,18 +339,24 @@ void SMX::SMXDevice::SendConfig() // 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 - // to the packet later on brought it up to 126, so the maximum was raised to 250. - // Older firmware won't use the extra fields, but will ignore the packet if it's - // larger than it supports, so just truncate the packet for these devices to make - // sure this doesn't happen. - if(config.masterVersion <= 3) - iSize = min(iSize, offsetof(SMXConfig, flags)); - - sData.append((char *) &iSize, sizeof(iSize)); - sData.append((char *) &wanted_config, sizeof(wanted_config)); + + // Append the config packet. + if(deviceInfo.m_iFirmwareVersion < 5) + { + // Convert wanted_config to the old configuration format. + vector outputConfig = rawConfig; + ConvertToOldConfig(wanted_config, outputConfig); + + uint8_t iSize = (uint8_t) outputConfig.size(); + sData.append((char *) &iSize, sizeof(iSize)); + sData.append((char *) outputConfig.data(), outputConfig.size()); + } + else + { + int8_t iSize = sizeof(SMXConfig); + sData.append((char *) &iSize, sizeof(iSize)); + sData.append((char *) &wanted_config, sizeof(wanted_config)); + } // Don't send another config packet until this one finishes, so if we get a bunch of // SetConfig calls quickly we won't spam the device, which can get slow. diff --git a/sdk/Windows/SMXDevice.h b/sdk/Windows/SMXDevice.h index ce9ef9c..44c2f0f 100644 --- a/sdk/Windows/SMXDevice.h +++ b/sdk/Windows/SMXDevice.h @@ -103,6 +103,7 @@ private: // The configuration we've read from the device. m_bHaveConfig is true if we've received // a configuration from the device since we've connected to it. SMXConfig config; + vector rawConfig; bool m_bHaveConfig = false; // This is the configuration the user has set, if he's changed anything. We send this to