示例#1
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);
        }
示例#2
0
        /// <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()));
        }
示例#3
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);
         }
     }
 }
示例#4
0
        /// <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);
            }
        }
示例#5
0
        /// <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());
            }
        }
示例#6
0
        /// <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());
            }
        }
示例#7
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);
        }
示例#8
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);
        }