/// <summary> /// Writes a SmcSettings object as text to the specified streamwriter. /// </summary> /// <param name="settings">The settings to read from.</param> /// <param name="filename">The file to write to.</param> /// <param name="device">The device that these settings came from (optional!). Adds extra comments to the file.</param> public static void save(SmcSettings settings, string filename, Smc device) { using (FileStream settingsFile = File.Open(filename, FileMode.Create)) { using (StreamWriter sw = new StreamWriter(settingsFile)) { save(settings, sw, device); } } }
/// <summary> /// Compares this object to another to see if they have the same values. /// </summary> public override bool Equals(object x) { SmcSettings s = x as SmcSettings; if (s == null) { return(false); } return(this.convertToStruct().Equals(s.convertToStruct())); }
/// <summary> /// Fixes certain thigns about a setttings object so that it doesn't make the device /// do something invalid. /// For each thing that gets fixed, a warning is added to the warnings list that is passed in. /// </summary> /// <param name="newSettings">The settings to fix.</param> /// <param name="warnings">A list of warnings. This function will add items to the list.</param> /// <param name="productId">The product ID of the device these settings will be used for. /// If unknown, this argument should be 0.</param> public static void fixSettings(SmcSettings newSettings, List <string> warnings, UInt16 productId) { if (newSettings.overTempMax < newSettings.overTempMin) { warnings.Add("The over-temperature minimum was lower than the over-temperature maximum. " + "Both settings will be set to " + Smc.temperatureToString(newSettings.overTempMax) + " so the motor will shut off at that temperature."); newSettings.overTempMin = newSettings.overTempMax; } if (newSettings.lowVinStartupMv < newSettings.lowVinShutoffMv) { if (newSettings.lowVinShutoffMv + 500 > UInt16.MaxValue) { newSettings.lowVinStartupMv = UInt16.MaxValue; } else { newSettings.lowVinStartupMv = (UInt16)(newSettings.lowVinShutoffMv + 500); } warnings.Add("The Low VIN Startup voltage was lower than the Low VIN Shutoff voltage (" + newSettings.lowVinShutoffMv / (decimal)1000 + " V). " + "The Low VIN Startup voltage will be changed to " + newSettings.lowVinStartupMv / (decimal)1000 + " V."); } if (newSettings.highVinShutoffMv < newSettings.lowVinStartupMv) { newSettings.highVinShutoffMv = (new SmcSettings(productId).highVinShutoffMv); warnings.Add("The High VIN Shutoff voltage was lower than the Low VIN Startup voltage (" + newSettings.lowVinStartupMv / (decimal)1000 + " V). " + "The High VIN Shutoff voltage will be changed to " + newSettings.highVinShutoffMv / (decimal)1000 + " V."); } // Prevent the channel scaling values from being out of order (it's okay if they are equal) foreach (SmcChannel channel in Smc.channels) { SmcChannelSettings cs = newSettings.getChannelSettings(channel); if (cs.errorMin > cs.inputMin || cs.inputMin > cs.inputNeutralMin || cs.inputNeutralMin > cs.inputNeutralMax || cs.inputNeutralMax > cs.inputMax || cs.inputMax > cs.errorMax) { warnings.Add("The scaling values for " + channel.name() + " are out of order. They will be reset to their default settings."); SmcChannelSettings defaults = SmcChannelSettings.defaults(channel); cs.errorMin = defaults.errorMin; cs.inputMin = defaults.inputMin; cs.inputNeutralMin = defaults.inputNeutralMin; cs.inputNeutralMax = defaults.inputNeutralMax; cs.inputMax = defaults.inputMax; cs.errorMax = defaults.errorMax; } } fixMotorLimits(newSettings.forwardLimits, "forward", warnings); fixMotorLimits(newSettings.reverseLimits, "reverse", warnings); }
/// <summary> /// Sends a new set of settings to the device, to be written to flash. /// This takes about 26 ms. /// </summary> public unsafe void setSmcSettings(SmcSettings settings) { SmcSettingsStruct settingsStruct = settings.convertToStruct(); try { controlTransfer(0x40, (byte)SmcRequest.SetSettings, 0, 0, &settingsStruct, (UInt16)sizeof(SmcSettingsStruct)); } catch (Exception exception) { throw new Exception("There was an error writing settings to the device.", exception); } }
/// <summary> /// Changes the settings for a specified channel. /// </summary> /// <param name="settings">The settings of the device. This object will be modified.</param> /// <param name="channel">Specifies the channel to change.</param> /// <param name="channelSettings">The new settings for the channel.</param> public static void setChannelSettings(this SmcSettings settings, SmcChannel channel, SmcChannelSettings channelSettings) { switch (channel) { case SmcChannel.Analog1: settings.analog1 = channelSettings; break; case SmcChannel.Analog2: settings.analog2 = channelSettings; break; case SmcChannel.Rc1: settings.rc1 = channelSettings; break; case SmcChannel.Rc2: settings.rc2 = channelSettings; break; default: throw new Exception("Unknown Channel: " + channel.ToString()); } }
/// <summary> /// Gets the settings for the specified channel. /// </summary> /// <param name="settings">The settings of the device.</param> /// <param name="channel">Specifies what channel to fetch.</param> /// <returns>The settings of the specified channel.</returns> public static SmcChannelSettings getChannelSettings(this SmcSettings settings, SmcChannel channel) { switch (channel) { case SmcChannel.Analog1: return(settings.analog1); case SmcChannel.Analog2: return(settings.analog2); case SmcChannel.Rc1: return(settings.rc1); case SmcChannel.Rc2: return(settings.rc2); default: throw new Exception("Unknown Channel: " + channel.ToString()); } }
/// <summary> /// Writes a SmcSettings object as text to the specified streamwriter. /// </summary> /// <param name="settings">The settings to read from.</param> /// <param name="sw">The file to write to.</param> /// <param name="device">The device that these settings came from (optional!). Adds extra comments to the file.</param> public static void save(SmcSettings settings, StreamWriter sw, Smc device) { XmlTextWriter writer = new XmlTextWriter(sw); writer.Formatting = Formatting.Indented; writer.WriteComment("Pololu Simple Motor Controller settings file. http://www.pololu.com/docs/0J44"); writer.WriteComment("Created on: " + DateTime.Now.ToString("u").TrimEnd('Z')); // save local time in a sortable format if (device != null) { writer.WriteComment("Device model: " + Smc.productIdToLongModelString(device.productId)); writer.WriteComment("Device serial number: " + device.getSerialNumber()); writer.WriteComment("Device firmware version: " + device.getFirmwareVersionString()); } writer.WriteStartElement("SmcSettings"); // XML file version, so that we can parse old XML types in the future and old versions of the // configuration utility don't try to read newer settings files that have a different format. writer.WriteAttributeString("version", "1"); // [Add-new-settings-here] writer.WriteElementString("InputMode", settings.inputMode.ToString()); writer.WriteElementString("MixingMode", settings.mixingMode.ToString()); writer.WriteElementBool("DisableSafeStart", settings.disableSafeStart); writer.WriteElementBool("IgnorePotDisconnect", settings.ignorePotDisconnect); writer.WriteElementBool("IgnoreErrLineHigh", settings.ignoreErrLineHigh); writer.WriteElementBool("NeverSuspend", settings.neverSuspend); writer.WriteElementBool("TempLimitGradual", settings.tempLimitGradual); writer.WriteElementRange("OverTemp", settings.overTempMin, settings.overTempMax); writer.WriteElementU32("LowVinShutoffTimeout", settings.lowVinShutoffTimeout); writer.WriteElementU32("LowVinShutoffMv", settings.lowVinShutoffMv); writer.WriteElementU32("LowVinStartupMv", settings.lowVinStartupMv); writer.WriteElementU32("HighVinShutoffMv", settings.highVinShutoffMv); writer.WriteElementString("SerialMode", settings.serialMode.ToString()); writer.WriteElementU32("SerialDeviceNumber", settings.serialDeviceNumber); writer.WriteElementU32("CommandTimeout", settings.commandTimeout); writer.WriteElementString("CrcMode", settings.crcMode.ToString()); writer.WriteElementBool("UartResponseDelay", settings.uartResponseDelay); writer.WriteElementBool("UseFixedBaudRate", settings.useFixedBaudRate); writer.WriteElementU32("FixedBaudRate", settings.fixedBaudRateBps); writer.WriteComment("Input Settings"); writer.WriteElementChannelSettings(SmcChannel.Rc1, settings.rc1); writer.WriteElementChannelSettings(SmcChannel.Rc2, settings.rc2); writer.WriteElementChannelSettings(SmcChannel.Analog1, settings.analog1); writer.WriteElementChannelSettings(SmcChannel.Analog2, settings.analog2); writer.WriteComment("Motor Settings"); writer.WriteElementU32("PwmPeriodFactor", settings.pwmPeriodFactor); writer.WriteElementBool("MotorInvert", settings.motorInvert); writer.WriteElementU32("SpeedZeroBrakeAmount", settings.speedZeroBrakeAmount); writer.WriteElementU32("SpeedUpdatePeriod", settings.speedUpdatePeriod); writer.WriteElementMotorLimits("ForwardLimits", settings.forwardLimits); writer.WriteElementMotorLimits("ReverseLimits", settings.reverseLimits); writer.WriteComment("Advanced Settings"); writer.WriteElementRange("PulsePeriod", settings.minPulsePeriod, settings.maxPulsePeriod); writer.WriteElementU32("RcTimeout", settings.rcTimeout); writer.WriteElementU32("ConsecGoodPulses", settings.consecGoodPulses); writer.WriteElementS32("VinMultiplierOffset", settings.vinMultiplierOffset); writer.WriteEndElement(); // End SmcSettings tag. }
/// <summary> /// Parses a saved configuration file and returns a SmcSettings object. /// </summary> /// <param name="warnings">A list of warnings. Whenever something goes /// wrong with the file loading, a warning will be added to this list. /// The warnings are not fatal; if the function returns it will return /// a valid SmcSettings object. /// </param> /// <param name="sr">The file to read from.</param> /// <param name="productId">The product id of the device we are loading these /// settings for. Pass 0 if it is unknown at this time.</param> public static SmcSettings load(StreamReader sr, List <String> warnings, UInt16 productId) { XmlReader reader = XmlReader.Create(sr); reader.ReadToFollowing("SmcSettings"); Tag settingsTag = Tag.readFrom(reader, warnings); reader.Close(); SmcSettings settings = new SmcSettings(productId); // [Add-new-settings-here] if (assertValue(settingsTag, "InputMode", warnings)) { try { settings.inputMode = (SmcInputMode)Enum.Parse(typeof(SmcInputMode), settingsTag.values["InputMode"]); } catch { warnings.Add("Invalid InputMode value \"" + settingsTag.values["InputMode"] + "\"."); } } if (assertValue(settingsTag, "MixingMode", warnings)) { try { settings.mixingMode = (SmcMixingMode)Enum.Parse(typeof(SmcMixingMode), settingsTag.values["MixingMode"]); } catch { warnings.Add("Invalid MixingMode value \"" + settingsTag.values["MixingMode"] + "\"."); } } parseBool(settingsTag, "DisableSafeStart", ref settings.disableSafeStart, warnings); parseBool(settingsTag, "IgnorePotDisconnect", ref settings.ignorePotDisconnect, warnings); parseBool(settingsTag, "IgnoreErrLineHigh", ref settings.ignoreErrLineHigh, warnings); parseBool(settingsTag, "NeverSuspend", ref settings.neverSuspend, warnings); parseBool(settingsTag, "TempLimitGradual", ref settings.tempLimitGradual, warnings); parseRange(settingsTag, "OverTemp", ref settings.overTempMin, ref settings.overTempMax, warnings); parseU16(settingsTag, "LowVinShutoffTimeout", ref settings.lowVinShutoffTimeout, warnings); parseU16(settingsTag, "LowVinShutoffMv", ref settings.lowVinShutoffMv, warnings); parseU16(settingsTag, "LowVinStartupMv", ref settings.lowVinStartupMv, warnings); parseU16(settingsTag, "HighVinShutoffMv", ref settings.highVinShutoffMv, warnings); if (assertValue(settingsTag, "SerialMode", warnings)) { try { settings.serialMode = (SmcSerialMode)Enum.Parse(typeof(SmcSerialMode), settingsTag.values["SerialMode"]); } catch { warnings.Add("Invalid SmcSerialMode value \"" + settingsTag.values["SerialMode"] + "\"."); } } parseU8(settingsTag, "SerialDeviceNumber", ref settings.serialDeviceNumber, warnings); parseU16(settingsTag, "CommandTimeout", ref settings.commandTimeout, warnings); if (assertValue(settingsTag, "CrcMode", warnings)) { try { settings.crcMode = (SmcCrcMode)Enum.Parse(typeof(SmcCrcMode), settingsTag.values["CrcMode"]); } catch { warnings.Add("Invalid CrcMode value \"" + settingsTag.values["CrcMode"] + "\"."); } } parseBool(settingsTag, "UartResponseDelay", ref settings.uartResponseDelay, warnings); parseBool(settingsTag, "UseFixedBaudRate", ref settings.useFixedBaudRate, warnings); parseU32(settingsTag, "FixedBaudRate", ref settings.fixedBaudRateBps, warnings); parseChannel(settingsTag, SmcChannel.Rc1, "Rc1", settings.rc1, warnings); parseChannel(settingsTag, SmcChannel.Rc2, "Rc2", settings.rc2, warnings); parseChannel(settingsTag, SmcChannel.Analog1, "Analog1", settings.analog1, warnings); parseChannel(settingsTag, SmcChannel.Analog2, "Analog2", settings.analog2, warnings); parseU8(settingsTag, "PwmPeriodFactor", ref settings.pwmPeriodFactor, warnings); parseBool(settingsTag, "MotorInvert", ref settings.motorInvert, warnings); parseU8(settingsTag, "SpeedZeroBrakeAmount", ref settings.speedZeroBrakeAmount, warnings); parseU16(settingsTag, "SpeedUpdatePeriod", ref settings.speedUpdatePeriod, warnings); parseLimits(settingsTag, "ForwardLimits", settings.forwardLimits, warnings); parseLimits(settingsTag, "ReverseLimits", settings.reverseLimits, warnings); parseRange(settingsTag, "PulsePeriod", ref settings.minPulsePeriod, ref settings.maxPulsePeriod, warnings); parseU16(settingsTag, "RcTimeout", ref settings.rcTimeout, warnings); parseU8(settingsTag, "ConsecGoodPulses", ref settings.consecGoodPulses, warnings); parseS16(settingsTag, "VinMultiplierOffset", ref settings.vinMultiplierOffset, warnings); return(settings); }
/// <summary> /// Parses a saved configuration file and returns a SmcSettings object. /// </summary> /// <param name="warnings">A list of warnings. Whenever something goes /// wrong with the file loading, a warning will be added to this list. /// The warnings are not fatal; if the function returns it will return /// a valid SmcSettings object. /// </param> /// <param name="sr">The file to read from.</param> /// <param name="productId">The product id of the device we are loading these /// settings for. Pass 0 if it is unknown at this time.</param> public static SmcSettings load(StreamReader sr, List<String> warnings, UInt16 productId) { XmlReader reader = XmlReader.Create(sr); reader.ReadToFollowing("SmcSettings"); Tag settingsTag = Tag.readFrom(reader, warnings); reader.Close(); SmcSettings settings = new SmcSettings(productId); // [Add-new-settings-here] if (assertValue(settingsTag, "InputMode", warnings)) { try { settings.inputMode = (SmcInputMode)Enum.Parse(typeof(SmcInputMode), settingsTag.values["InputMode"]); } catch { warnings.Add("Invalid InputMode value \"" + settingsTag.values["InputMode"] + "\"."); } } if (assertValue(settingsTag, "MixingMode", warnings)) { try { settings.mixingMode = (SmcMixingMode)Enum.Parse(typeof(SmcMixingMode), settingsTag.values["MixingMode"]); } catch { warnings.Add("Invalid MixingMode value \"" + settingsTag.values["MixingMode"] + "\"."); } } parseBool(settingsTag, "DisableSafeStart", ref settings.disableSafeStart, warnings); parseBool(settingsTag, "IgnorePotDisconnect", ref settings.ignorePotDisconnect, warnings); parseBool(settingsTag, "IgnoreErrLineHigh", ref settings.ignoreErrLineHigh, warnings); parseBool(settingsTag, "NeverSuspend", ref settings.neverSuspend, warnings); parseBool(settingsTag, "TempLimitGradual", ref settings.tempLimitGradual, warnings); parseRange(settingsTag, "OverTemp", ref settings.overTempMin, ref settings.overTempMax, warnings); parseU16(settingsTag, "LowVinShutoffTimeout", ref settings.lowVinShutoffTimeout, warnings); parseU16(settingsTag, "LowVinShutoffMv", ref settings.lowVinShutoffMv, warnings); parseU16(settingsTag, "LowVinStartupMv", ref settings.lowVinStartupMv, warnings); parseU16(settingsTag, "HighVinShutoffMv", ref settings.highVinShutoffMv, warnings); if (assertValue(settingsTag, "SerialMode", warnings)) { try { settings.serialMode = (SmcSerialMode)Enum.Parse(typeof(SmcSerialMode), settingsTag.values["SerialMode"]); } catch { warnings.Add("Invalid SmcSerialMode value \"" + settingsTag.values["SerialMode"] + "\"."); } } parseU8(settingsTag, "SerialDeviceNumber", ref settings.serialDeviceNumber, warnings); parseU16(settingsTag, "CommandTimeout", ref settings.commandTimeout, warnings); if (assertValue(settingsTag, "CrcMode", warnings)) { try { settings.crcMode = (SmcCrcMode)Enum.Parse(typeof(SmcCrcMode), settingsTag.values["CrcMode"]); } catch { warnings.Add("Invalid CrcMode value \"" + settingsTag.values["CrcMode"] + "\"."); } } parseBool(settingsTag, "UartResponseDelay", ref settings.uartResponseDelay, warnings); parseBool(settingsTag, "UseFixedBaudRate", ref settings.useFixedBaudRate, warnings); parseU32(settingsTag, "FixedBaudRate", ref settings.fixedBaudRateBps, warnings); parseChannel(settingsTag, SmcChannel.Rc1, "Rc1", settings.rc1, warnings); parseChannel(settingsTag, SmcChannel.Rc2, "Rc2", settings.rc2, warnings); parseChannel(settingsTag, SmcChannel.Analog1, "Analog1", settings.analog1, warnings); parseChannel(settingsTag, SmcChannel.Analog2, "Analog2", settings.analog2, warnings); parseU8(settingsTag, "PwmPeriodFactor", ref settings.pwmPeriodFactor, warnings); parseBool(settingsTag, "MotorInvert", ref settings.motorInvert, warnings); parseU8(settingsTag, "SpeedZeroBrakeAmount", ref settings.speedZeroBrakeAmount, warnings); parseU16(settingsTag, "SpeedUpdatePeriod", ref settings.speedUpdatePeriod, warnings); parseLimits(settingsTag, "ForwardLimits", settings.forwardLimits, warnings); parseLimits(settingsTag, "ReverseLimits", settings.reverseLimits, warnings); parseRange(settingsTag, "PulsePeriod", ref settings.minPulsePeriod, ref settings.maxPulsePeriod, warnings); parseU16(settingsTag, "RcTimeout", ref settings.rcTimeout, warnings); parseU8(settingsTag, "ConsecGoodPulses", ref settings.consecGoodPulses, warnings); parseS16(settingsTag, "VinMultiplierOffset", ref settings.vinMultiplierOffset, warnings); return settings; }
/// <summary> /// Fixes certain thigns about a setttings object so that it doesn't make the device /// do something invalid. /// For each thing that gets fixed, a warning is added to the warnings list that is passed in. /// </summary> /// <param name="newSettings">The settings to fix.</param> /// <param name="warnings">A list of warnings. This function will add items to the list.</param> /// <param name="productId">The product ID of the device these settings will be used for. /// If unknown, this argument should be 0.</param> public static void fixSettings(SmcSettings newSettings, List<string> warnings, UInt16 productId) { if (newSettings.overTempMax < newSettings.overTempMin) { warnings.Add("The over-temperature minimum was lower than the over-temperature maximum. " + "Both settings will be set to " + Smc.temperatureToString(newSettings.overTempMax) + " so the motor will shut off at that temperature."); newSettings.overTempMin = newSettings.overTempMax; } if (newSettings.lowVinStartupMv < newSettings.lowVinShutoffMv) { if (newSettings.lowVinShutoffMv + 500 > UInt16.MaxValue) { newSettings.lowVinStartupMv = UInt16.MaxValue; } else { newSettings.lowVinStartupMv = (UInt16)(newSettings.lowVinShutoffMv + 500); } warnings.Add("The Low VIN Startup voltage was lower than the Low VIN Shutoff voltage (" + newSettings.lowVinShutoffMv/(decimal)1000 + " V). " + "The Low VIN Startup voltage will be changed to " + newSettings.lowVinStartupMv/(decimal)1000 + " V."); } if (newSettings.highVinShutoffMv < newSettings.lowVinStartupMv) { newSettings.highVinShutoffMv = (new SmcSettings(productId).highVinShutoffMv); warnings.Add("The High VIN Shutoff voltage was lower than the Low VIN Startup voltage (" + newSettings.lowVinStartupMv / (decimal)1000 + " V). " + "The High VIN Shutoff voltage will be changed to " + newSettings.highVinShutoffMv / (decimal)1000 + " V."); } // Prevent the channel scaling values from being out of order (it's okay if they are equal) foreach(SmcChannel channel in Smc.channels) { SmcChannelSettings cs = newSettings.getChannelSettings(channel); if (cs.errorMin > cs.inputMin || cs.inputMin > cs.inputNeutralMin || cs.inputNeutralMin > cs.inputNeutralMax || cs.inputNeutralMax > cs.inputMax || cs.inputMax > cs.errorMax) { warnings.Add("The scaling values for " + channel.name() + " are out of order. They will be reset to their default settings."); SmcChannelSettings defaults = SmcChannelSettings.defaults(channel); cs.errorMin = defaults.errorMin; cs.inputMin = defaults.inputMin; cs.inputNeutralMin = defaults.inputNeutralMin; cs.inputNeutralMax = defaults.inputNeutralMax; cs.inputMax = defaults.inputMax; cs.errorMax = defaults.errorMax; } } fixMotorLimits(newSettings.forwardLimits, "forward", warnings); fixMotorLimits(newSettings.reverseLimits, "reverse", warnings); }