예제 #1
0
        /// <summary>
        /// Gets a list of devices that are currently connected to USB.
        /// </summary>
        public static List <DeviceListItem> getConnectedDevices()
        {
            List <DeviceListItem> devices;

            try
            {
                try
                {
                    devices = UsbDevice.getDeviceList(Smc.deviceInterfaceGuid);
                }
                catch (NotImplementedException)
                {
                    // use vendor and product instead
                    devices = UsbDevice.getDeviceList(Smc.vendorID, Smc.productIDs);
                }
            }
            catch (Exception e)
            {
                throw new Exception("There was an error getting the list of connected devices.", e);
            }

            foreach (DeviceListItem dli in devices)
            {
                Smc.setDeviceListItemText(dli);
            }

            return(devices);
        }
예제 #2
0
        /// <summary>
        /// Gets a list of bootloaders that are currently connected to USB.
        /// </summary>
        public static List <DeviceListItem> getConnectedBootloaders()
        {
            Guid bootloaderGuid = new Guid("82959cfa-7a2d-431f-a9a1-500b55d90950");

            List <DeviceListItem> bootloaders;

            try
            {
                List <UInt16>         bootloaderProductIDs = new List <UInt16>(Smc.bootloaderProductIDs);
                List <DeviceListItem> allBootloaders       = UsbDevice.getDeviceList(bootloaderGuid);
                bootloaders = new List <DeviceListItem>();
                foreach (DeviceListItem dli in allBootloaders)
                {
                    if (bootloaderProductIDs.Contains(dli.productId))
                    {
                        bootloaders.Add(dli);
                    }
                }
            }
            catch (NotImplementedException)
            {
                // use vendor and product instead
                bootloaders = UsbDevice.getDeviceList(0x1FFB, Smc.bootloaderProductIDs);
            }

            foreach (DeviceListItem dli in bootloaders)
            {
                Smc.setDeviceListItemText(dli);
            }

            return(bootloaders);
        }
예제 #3
0
        /// <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.</param>
        public static void save(SmcSettings settings, StreamWriter sw, Smc device)
        {
            sw.WriteLine("# Pololu Simple Motor Controller G2 settings file.");
            sw.WriteLine("# " + Smc.documentationUrl);
            sw.WriteLine("product: " + Smc.productIdToShortModelString(device.productId));

            // [Add-new-settings-here]

            writeInputMode(sw, settings.inputMode);
            writeMixingMode(sw, settings.mixingMode);
            writeSerialMode(sw, settings.serialMode);
            writeBool(sw, "enable_i2c", settings.enableI2C);
            writeU32(sw, "serial_device_number", settings.serialDeviceNumber);
            writeBool(sw, "crc_for_commands", settings.crcForCommands);
            writeBool(sw, "crc_for_responses", settings.crcForResponses);
            writeBool(sw, "uart_response_delay", settings.uartResponseDelay);
            writeBool(sw, "use_fixed_baud_rate", settings.useFixedBaudRate);
            writeU32(sw, "fixed_baud_rate", settings.fixedBaudRateBps);

            // Channel settings
            writeChannelSettings(sw, "rc1", false, settings.rc1);
            writeChannelSettings(sw, "rc2", false, settings.rc2);
            writeChannelSettings(sw, "analog1", true, settings.analog1);
            writeChannelSettings(sw, "analog2", true, settings.analog2);

            // Motor settings
            writeU32(sw, "pwm_period_factor", settings.pwmPeriodFactor);
            writeBool(sw, "motor_invert", settings.motorInvert);
            writeBool(sw, "coast_when_off", settings.coastWhenOff);
            writeU32(sw, "speed_update_period", settings.speedUpdatePeriod);
            writeMotorLimits(sw, "forward", settings.forwardLimits);
            writeMotorLimits(sw, "reverse", settings.reverseLimits);

            writeU32(sw, "current_limit", settings.currentLimit);
            writeU32(sw, "current_offset_calibration", settings.currentOffsetCalibration);
            writeU32(sw, "current_scale_calibration", settings.currentScaleCalibration);

            // Advanced settings
            writeU32(sw, "min_pulse_period", settings.minPulsePeriod);
            writeU32(sw, "max_pulse_period", settings.maxPulsePeriod);
            writeU32(sw, "rc_timeout", settings.rcTimeout);
            writeU32(sw, "consec_good_pulses", settings.consecGoodPulses);
            writeS32(sw, "vin_scale_calibration", settings.vinScaleCalibration);

            writeBool(sw, "temp_limit_gradual", settings.tempLimitGradual);
            writeU32(sw, "over_temp_complete_shutoff_threshold", settings.overTempCompleteShutoffThreshold);
            writeU32(sw, "over_temp_normal_operation_threshold", settings.overTempNormalOperationThreshold);
            writeU32(sw, "low_vin_shutoff_timeout", settings.lowVinShutoffTimeout);
            writeU32(sw, "low_vin_shutoff_mv", settings.lowVinShutoffMv);
            writeU32(sw, "low_vin_startup_mv", settings.lowVinStartupMv);
            writeU32(sw, "high_vin_shutoff_mv", settings.highVinShutoffMv);

            writeBool(sw, "disable_safe_start", settings.disableSafeStart);
            writeBool(sw, "ignore_pot_disconnect", settings.ignorePotDisconnect);
            writeBool(sw, "ignore_err_line_high", settings.ignoreErrLineHigh);
            writeBool(sw, "never_sleep", settings.neverSleep);
            writeU32(sw, "command_timeout", settings.commandTimeout);
        }
예제 #4
0
 /// <summary>
 /// Writes a SmcSettings object as text to the specified file.
 /// </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.</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);
         }
     }
 }
예제 #5
0
 internal SmcSettings(UInt16 productId, SmcSettingsStruct s)
 {
     this.productId = productId;
     // [Add-new-settings-here]
     this.neverSleep          = (s.b1 & SmcBoolSettings1.NeverSleep) != 0;
     this.uartResponseDelay   = (s.b1 & SmcBoolSettings1.UartResponseDelay) != 0;
     this.useFixedBaudRate    = (s.b1 & SmcBoolSettings1.UseFixedBaudRate) != 0;
     this.disableSafeStart    = (s.b1 & SmcBoolSettings1.DisableSafeStart) != 0;
     this.enableI2C           = (s.b1 & SmcBoolSettings1.EnableI2C) != 0;
     this.ignoreErrLineHigh   = (s.b1 & SmcBoolSettings1.IgnoreErrLineHigh) != 0;
     this.tempLimitGradual    = (s.b1 & SmcBoolSettings1.TempLimitGradual) != 0;
     this.ignorePotDisconnect = (s.b1 & SmcBoolSettings1.IgnorePotDisconnect) != 0;
     this.motorInvert         = (s.b1 & SmcBoolSettings1.MotorInvert) != 0;
     this.coastWhenOff        = (s.b1 & SmcBoolSettings1.CoastWhenOff) != 0;
     this.crcForCommands      = (s.b1 & SmcBoolSettings1.CrcForCommands) != 0;
     this.crcForResponses     = (s.b1 & SmcBoolSettings1.CrcForResponses) != 0;
     this.fixedBaudRateBps    = Smc.convertBaudRegisterToBps(s.fixedBaudRateRegister);
     this.speedUpdatePeriod   = s.speedUpdatePeriod;
     this.commandTimeout      = s.commandTimeout;
     this.serialDeviceNumber  = s.serialDeviceNumber;
     this.overTempCompleteShutoffThreshold = s.overTempCompleteShutoffThreshold;
     this.overTempNormalOperationThreshold = s.overTempNormalOperationThreshold;
     this.inputMode            = s.inputMode;
     this.pwmPeriodFactor      = s.pwmPeriodFactor;
     this.mixingMode           = s.mixingMode;
     this.minPulsePeriod       = s.minPulsePeriod;
     this.maxPulsePeriod       = s.maxPulsePeriod;
     this.rcTimeout            = s.rcTimeout;
     this.consecGoodPulses     = s.consecGoodPulses;
     this.vinScaleCalibration  = s.vinScaleCalibration;
     this.lowVinShutoffTimeout = s.lowVinShutoffTimeout;
     this.lowVinShutoffMv      = s.lowVinShutoffMv;
     this.lowVinStartupMv      = s.lowVinStartupMv;
     this.highVinShutoffMv     = s.highVinShutoffMv;
     this.serialMode           = s.serialMode;
     this.rc1                      = new SmcChannelSettings(s.rc1);
     this.rc2                      = new SmcChannelSettings(s.rc2);
     this.analog1                  = new SmcChannelSettings(s.analog1);
     this.analog2                  = new SmcChannelSettings(s.analog2);
     this.forwardLimits            = new SmcMotorLimits(s.forwardLimits);
     this.reverseLimits            = new SmcMotorLimits(s.reverseLimits);
     this.currentLimit             = s.currentLimit;
     this.currentOffsetCalibration = s.currentOffsetCalibration;
     this.currentScaleCalibration  = s.currentScaleCalibration;
 }
예제 #6
0
        /// <summary>
        /// Fixes certain things 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.</param>
        /// <param name="firmwareVersion">The firmware version 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, UInt16 firmwareVersion)
        {
            if (productId == 0)
            {
                throw new Exception("Internal error: an invalid product ID was passed to fixSettings.");
            }

            if (newSettings.productId != productId)
            {
                throw new Exception(
                          "These settings are for a different device.  " +
                          "The settings are for the " + Smc.productIdToShortModelString(newSettings.productId) + ", " +
                          "not the " + Smc.productIdToShortModelString(productId) + ".");
            }

            // TODO: change the messages here to use present tense and future tense (like the Tic and Jrk).

            if (newSettings.overTempCompleteShutoffThreshold < newSettings.overTempNormalOperationThreshold)
            {
                warnings.Add(
                    "The over-temperature complete shutoff threshold was " +
                    "lower than the over-temperature normal operation threshold.  " +
                    "Both settings will be set to " +
                    Smc.temperatureToString(newSettings.overTempCompleteShutoffThreshold) +
                    " so the motor will shut off at that temperature.");
                newSettings.overTempNormalOperationThreshold = newSettings.overTempCompleteShutoffThreshold;
            }

            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.");
            }

            if (newSettings.vinScaleCalibration > 1500)
            {
                newSettings.vinScaleCalibration = 1500;
                warnings.Add("The VIN scale calibration was too high.  It will be changed to 1500.");
            }

            if (newSettings.currentScaleCalibration > 20000)
            {
                newSettings.currentScaleCalibration = 20000;
                warnings.Add("The current scale calibration was too high.  It will be changed to 20000.");
            }

            // 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);
        }
예제 #7
0
        internal SmcSettingsStruct convertToStruct()
        {
            SmcSettingsStruct s = new SmcSettingsStruct();

            // [Add-new-settings-here]
            if (this.neverSleep)
            {
                s.b1 |= SmcBoolSettings1.NeverSleep;
            }
            if (this.uartResponseDelay)
            {
                s.b1 |= SmcBoolSettings1.UartResponseDelay;
            }
            if (this.useFixedBaudRate)
            {
                s.b1 |= SmcBoolSettings1.UseFixedBaudRate;
            }
            if (this.disableSafeStart)
            {
                s.b1 |= SmcBoolSettings1.DisableSafeStart;
            }
            if (this.enableI2C)
            {
                s.b1 |= SmcBoolSettings1.EnableI2C;
            }
            if (this.ignoreErrLineHigh)
            {
                s.b1 |= SmcBoolSettings1.IgnoreErrLineHigh;
            }
            if (this.tempLimitGradual)
            {
                s.b1 |= SmcBoolSettings1.TempLimitGradual;
            }
            if (this.ignorePotDisconnect)
            {
                s.b1 |= SmcBoolSettings1.IgnorePotDisconnect;
            }
            if (this.motorInvert)
            {
                s.b1 |= SmcBoolSettings1.MotorInvert;
            }
            if (this.coastWhenOff)
            {
                s.b1 |= SmcBoolSettings1.CoastWhenOff;
            }
            if (this.crcForCommands)
            {
                s.b1 |= SmcBoolSettings1.CrcForCommands;
            }
            if (this.crcForResponses)
            {
                s.b1 |= SmcBoolSettings1.CrcForResponses;
            }
            s.fixedBaudRateRegister            = Smc.convertBpsToBaudRegister(this.fixedBaudRateBps);
            s.speedUpdatePeriod                = this.speedUpdatePeriod;
            s.commandTimeout                   = this.commandTimeout;
            s.serialDeviceNumber               = this.serialDeviceNumber;
            s.overTempCompleteShutoffThreshold = this.overTempCompleteShutoffThreshold;
            s.overTempNormalOperationThreshold = this.overTempNormalOperationThreshold;
            s.inputMode            = this.inputMode;
            s.pwmPeriodFactor      = this.pwmPeriodFactor;
            s.mixingMode           = this.mixingMode;
            s.minPulsePeriod       = this.minPulsePeriod;
            s.maxPulsePeriod       = this.maxPulsePeriod;
            s.rcTimeout            = this.rcTimeout;
            s.consecGoodPulses     = this.consecGoodPulses;
            s.vinScaleCalibration  = this.vinScaleCalibration;
            s.lowVinShutoffTimeout = this.lowVinShutoffTimeout;
            s.lowVinShutoffMv      = this.lowVinShutoffMv;
            s.lowVinStartupMv      = this.lowVinStartupMv;
            s.highVinShutoffMv     = this.highVinShutoffMv;
            s.serialMode           = this.serialMode;
            s.rc1                      = this.rc1.convertToStruct();
            s.rc2                      = this.rc2.convertToStruct();
            s.analog1                  = this.analog1.convertToStruct();
            s.analog2                  = this.analog2.convertToStruct();
            s.forwardLimits            = this.forwardLimits.convertToStruct();
            s.reverseLimits            = this.reverseLimits.convertToStruct();
            s.currentLimit             = this.currentLimit;
            s.currentOffsetCalibration = this.currentOffsetCalibration;
            s.currentScaleCalibration  = this.currentScaleCalibration;
            return(s);
        }
예제 #8
0
        /// <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>
        public static SmcSettings load(StreamReader sr, List <String> warnings)
        {
            // For now, we just ignore the 'product' line in the settings file.
            // Note: In the next version of this software, we probably want to do things
            // differently here.  We would not have a productId argument.  We would get the
            // product from the settings file itself and use that to populate the default settings.
            // We would have a "fix_and_change_product" function that takes care of switching the
            // settings over to the new product (and firmware version) and fixing any issues in
            // it at the same time.

            var map        = new Dictionary <string, string>();
            int lineNumber = 0;

            char[] separators = new[] { ':' };
            while (!sr.EndOfStream)
            {
                lineNumber++;
                string line = sr.ReadLine();
                if (line.Length == 0 || line.StartsWith("#"))
                {
                    continue;
                }

                string[] parts = line.Split(separators);

                // Make sure we have the right number of parts and make sure the first part does not
                // contain invalid characters that also make our later error messages about it be
                // confusing.
                if (parts.Length != 2 || parts[0].Contains(" ") || parts[0].Contains("\""))
                {
                    string hint = "";
                    if (line.StartsWith("<!--"))
                    {
                        hint = "  Settings files saved from original Simple Motor Controller software are not supported.";
                    }
                    throw new Exception("Line " + lineNumber + " has an invalid format." + hint);
                }

                map[parts[0]] = parts[1].Trim();
            }

            UInt16 productId = Smc.shortModelStringToProductId(map["product"]);

            if (productId == 0)
            {
                throw new Exception("Invalid product name: \"" + map["product"] + "\".");
            }
            SmcSettings settings = new SmcSettings(productId);

            foreach (var item in map)
            {
                string value = item.Value;
                string key   = item.Key;
                switch (key)
                {
                case "product":
                    // Already processed.
                    break;

                case "input_mode":
                    settings.inputMode = parseInputMode(value);
                    break;

                case "mixing_mode":
                    settings.mixingMode = parseMixingMode(value);
                    break;

                case "serial_mode":
                    settings.serialMode = parseSerialMode(value);
                    break;

                case "enable_i2c":
                    settings.enableI2C = parseBool(key, value);
                    break;

                case "serial_device_number":
                    settings.serialDeviceNumber = parseByte(key, value);
                    break;

                case "crc_for_commands":
                    settings.crcForCommands = parseBool(key, value);
                    break;

                case "crc_for_responses":
                    settings.crcForResponses = parseBool(key, value);
                    break;

                case "uart_response_delay":
                    settings.uartResponseDelay = parseBool(key, value);
                    break;

                case "use_fixed_baud_rate":
                    settings.useFixedBaudRate = parseBool(key, value);
                    break;

                case "fixed_baud_rate":
                    settings.fixedBaudRateBps = parseU32(key, value);
                    break;

                case "rc1_alternate_use":
                    settings.rc1.alternateUse = parseAlternateUse(key, value);
                    break;

                case "rc1_invert":
                    settings.rc1.invert = parseBool(key, value);
                    break;

                case "rc1_scaling_degree":
                    settings.rc1.scalingDegree = parseByte(key, value);
                    break;

                case "rc1_error_min":
                    settings.rc1.errorMin = parseU16(key, value);
                    break;

                case "rc1_input_min":
                    settings.rc1.inputMin = parseU16(key, value);
                    break;

                case "rc1_input_neutral_min":
                    settings.rc1.inputNeutralMin = parseU16(key, value);
                    break;

                case "rc1_input_neutral_max":
                    settings.rc1.inputNeutralMax = parseU16(key, value);
                    break;

                case "rc1_input_max":
                    settings.rc1.inputMax = parseU16(key, value);
                    break;

                case "rc1_error_max":
                    settings.rc1.errorMax = parseU16(key, value);
                    break;

                case "rc2_alternate_use":
                    settings.rc2.alternateUse = parseAlternateUse(key, value);
                    break;

                case "rc2_invert":
                    settings.rc2.invert = parseBool(key, value);
                    break;

                case "rc2_scaling_degree":
                    settings.rc2.scalingDegree = parseByte(key, value);
                    break;

                case "rc2_error_min":
                    settings.rc2.errorMin = parseU16(key, value);
                    break;

                case "rc2_input_min":
                    settings.rc2.inputMin = parseU16(key, value);
                    break;

                case "rc2_input_neutral_min":
                    settings.rc2.inputNeutralMin = parseU16(key, value);
                    break;

                case "rc2_input_neutral_max":
                    settings.rc2.inputNeutralMax = parseU16(key, value);
                    break;

                case "rc2_input_max":
                    settings.rc2.inputMax = parseU16(key, value);
                    break;

                case "rc2_error_max":
                    settings.rc2.errorMax = parseU16(key, value);
                    break;

                case "analog1_alternate_use":
                    settings.analog1.alternateUse = parseAlternateUse(key, value);
                    break;

                case "analog1_pin_mode":
                    settings.analog1.pinMode = parsePinMode(key, value);
                    break;

                case "analog1_invert":
                    settings.analog1.invert = parseBool(key, value);
                    break;

                case "analog1_scaling_degree":
                    settings.analog1.scalingDegree = parseByte(key, value);
                    break;

                case "analog1_error_min":
                    settings.analog1.errorMin = parseU16(key, value);
                    break;

                case "analog1_input_min":
                    settings.analog1.inputMin = parseU16(key, value);
                    break;

                case "analog1_input_neutral_min":
                    settings.analog1.inputNeutralMin = parseU16(key, value);
                    break;

                case "analog1_input_neutral_max":
                    settings.analog1.inputNeutralMax = parseU16(key, value);
                    break;

                case "analog1_input_max":
                    settings.analog1.inputMax = parseU16(key, value);
                    break;

                case "analog1_error_max":
                    settings.analog1.errorMax = parseU16(key, value);
                    break;

                case "analog2_alternate_use":
                    settings.analog2.alternateUse = parseAlternateUse(key, value);
                    break;

                case "analog2_pin_mode":
                    settings.analog2.pinMode = parsePinMode(key, value);
                    break;

                case "analog2_invert":
                    settings.analog2.invert = parseBool(key, value);
                    break;

                case "analog2_scaling_degree":
                    settings.analog2.scalingDegree = parseByte(key, value);
                    break;

                case "analog2_error_min":
                    settings.analog2.errorMin = parseU16(key, value);
                    break;

                case "analog2_input_min":
                    settings.analog2.inputMin = parseU16(key, value);
                    break;

                case "analog2_input_neutral_min":
                    settings.analog2.inputNeutralMin = parseU16(key, value);
                    break;

                case "analog2_input_neutral_max":
                    settings.analog2.inputNeutralMax = parseU16(key, value);
                    break;

                case "analog2_input_max":
                    settings.analog2.inputMax = parseU16(key, value);
                    break;

                case "analog2_error_max":
                    settings.analog2.errorMax = parseU16(key, value);
                    break;

                case "pwm_period_factor":
                    settings.pwmPeriodFactor = parseByte(key, value);
                    break;

                case "motor_invert":
                    settings.motorInvert = parseBool(key, value);
                    break;

                case "coast_when_off":
                    settings.coastWhenOff = parseBool(key, value);
                    break;

                case "speed_update_period":
                    settings.speedUpdatePeriod = parseU16(key, value);
                    break;

                case "forward_max_speed":
                    settings.forwardLimits.maxSpeed = parseU16(key, value);
                    break;

                case "forward_max_acceleration":
                    settings.forwardLimits.maxAcceleration = parseU16(key, value);
                    break;

                case "forward_max_deceleration":
                    settings.forwardLimits.maxDeceleration = parseU16(key, value);
                    break;

                case "forward_brake_duration":
                    settings.forwardLimits.brakeDuration = parseU16(key, value);
                    break;

                case "forward_starting_speed":
                    settings.forwardLimits.startingSpeed = parseU16(key, value);
                    break;

                case "reverse_max_speed":
                    settings.reverseLimits.maxSpeed = parseU16(key, value);
                    break;

                case "reverse_max_acceleration":
                    settings.reverseLimits.maxAcceleration = parseU16(key, value);
                    break;

                case "reverse_max_deceleration":
                    settings.reverseLimits.maxDeceleration = parseU16(key, value);
                    break;

                case "reverse_brake_duration":
                    settings.reverseLimits.brakeDuration = parseU16(key, value);
                    break;

                case "reverse_starting_speed":
                    settings.reverseLimits.startingSpeed = parseU16(key, value);
                    break;

                case "current_limit":
                    settings.currentLimit = parseU16(key, value);
                    break;

                case "current_offset_calibration":
                    settings.currentOffsetCalibration = parseU16(key, value);
                    break;

                case "current_scale_calibration":
                    settings.currentScaleCalibration = parseU16(key, value);
                    break;

                case "min_pulse_period":
                    settings.minPulsePeriod = parseU16(key, value);
                    break;

                case "max_pulse_period":
                    settings.maxPulsePeriod = parseU16(key, value);
                    break;

                case "rc_timeout":
                    settings.rcTimeout = parseU16(key, value);
                    break;

                case "consec_good_pulses":
                    settings.consecGoodPulses = parseByte(key, value);
                    break;

                case "vin_scale_calibration":
                    settings.vinScaleCalibration = parseU16(key, value);
                    break;

                case "temp_limit_gradual":
                    settings.tempLimitGradual = parseBool(key, value);
                    break;

                case "over_temp_complete_shutoff_threshold":
                    settings.overTempCompleteShutoffThreshold = parseU16(key, value);
                    break;

                case "over_temp_normal_operation_threshold":
                    settings.overTempNormalOperationThreshold = parseU16(key, value);
                    break;

                case "low_vin_shutoff_timeout":
                    settings.lowVinShutoffTimeout = parseU16(key, value);
                    break;

                case "low_vin_shutoff_mv":
                    settings.lowVinShutoffMv = parseU16(key, value);
                    break;

                case "low_vin_startup_mv":
                    settings.lowVinStartupMv = parseU16(key, value);
                    break;

                case "high_vin_shutoff_mv":
                    settings.highVinShutoffMv = parseU16(key, value);
                    break;

                case "disable_safe_start":
                    settings.disableSafeStart = parseBool(key, value);
                    break;

                case "ignore_pot_disconnect":
                    settings.ignorePotDisconnect = parseBool(key, value);
                    break;

                case "ignore_err_line_high":
                    settings.ignoreErrLineHigh = parseBool(key, value);
                    break;

                case "never_sleep":
                    settings.neverSleep = parseBool(key, value);
                    break;

                case "command_timeout":
                    settings.commandTimeout = parseU16(key, value);
                    break;

                default:
                    throw new Exception("Unrecognized key: \"" + key + "\".");
                }
            }

            // [Add-new-settings-here]

            // TODO: parse settings

            return(settings);
        }