001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package org.wpilib.hardware.rotation;
006
007import org.wpilib.hardware.discrete.AnalogInput;
008import org.wpilib.system.RobotController;
009import org.wpilib.util.sendable.Sendable;
010import org.wpilib.util.sendable.SendableBuilder;
011import org.wpilib.util.sendable.SendableRegistry;
012
013/**
014 * Class for reading analog potentiometers. Analog potentiometers read in an analog voltage that
015 * corresponds to a position. The position is in whichever units you choose, by way of the scaling
016 * and offset constants passed to the constructor.
017 */
018public class AnalogPotentiometer implements Sendable, AutoCloseable {
019  private AnalogInput m_analogInput;
020  private boolean m_initAnalogInput;
021  private double m_fullRange;
022  private double m_offset;
023
024  /**
025   * AnalogPotentiometer constructor.
026   *
027   * <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
028   * have a 270 degree potentiometer, and you want the output to be degrees with the halfway point
029   * as 0 degrees. The fullRange value is 270.0(degrees) and the offset is -135.0 since the halfway
030   * point after scaling is 135 degrees. This will calculate the result from the fullRange times the
031   * fraction of the supply voltage, plus the offset.
032   *
033   * @param channel The analog input channel this potentiometer is plugged into. 0-3 are on-board
034   *     and 4-7 are on the MXP port.
035   * @param fullRange The scaling to multiply the fraction by to get a meaningful unit.
036   * @param offset The offset to add to the scaled value for controlling the zero value
037   */
038  @SuppressWarnings("this-escape")
039  public AnalogPotentiometer(final int channel, double fullRange, double offset) {
040    this(new AnalogInput(channel), fullRange, offset);
041    m_initAnalogInput = true;
042    SendableRegistry.addChild(this, m_analogInput);
043  }
044
045  /**
046   * AnalogPotentiometer constructor.
047   *
048   * <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
049   * have a 270 degree potentiometer, and you want the output to be degrees with the halfway point
050   * as 0 degrees. The fullRange value is 270.0(degrees) and the offset is -135.0 since the halfway
051   * point after scaling is 135 degrees. This will calculate the result from the fullRange times the
052   * fraction of the supply voltage, plus the offset.
053   *
054   * @param input The {@link AnalogInput} this potentiometer is plugged into.
055   * @param fullRange The angular value (in desired units) representing the full 0-3.3V range of the
056   *     input.
057   * @param offset The angular value (in desired units) representing the angular output at 0V.
058   */
059  @SuppressWarnings("this-escape")
060  public AnalogPotentiometer(final AnalogInput input, double fullRange, double offset) {
061    SendableRegistry.add(this, "AnalogPotentiometer", input.getChannel());
062    m_analogInput = input;
063    m_initAnalogInput = false;
064
065    m_fullRange = fullRange;
066    m_offset = offset;
067  }
068
069  /**
070   * AnalogPotentiometer constructor.
071   *
072   * <p>Use the scale value so that the output produces meaningful values. I.E: you have a 270
073   * degree potentiometer, and you want the output to be degrees with the starting point as 0
074   * degrees. The scale value is 270.0(degrees).
075   *
076   * @param channel The analog input channel this potentiometer is plugged into. 0-3 are on-board
077   *     and 4-7 are on the MXP port.
078   * @param scale The scaling to multiply the voltage by to get a meaningful unit.
079   */
080  public AnalogPotentiometer(final int channel, double scale) {
081    this(channel, scale, 0);
082  }
083
084  /**
085   * AnalogPotentiometer constructor.
086   *
087   * <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
088   * have a 270 degree potentiometer, and you want the output to be degrees with the starting point
089   * as 0 degrees. The scale value is 270.0(degrees).
090   *
091   * @param input The {@link AnalogInput} this potentiometer is plugged into.
092   * @param scale The scaling to multiply the voltage by to get a meaningful unit.
093   */
094  public AnalogPotentiometer(final AnalogInput input, double scale) {
095    this(input, scale, 0);
096  }
097
098  /**
099   * AnalogPotentiometer constructor.
100   *
101   * <p>The potentiometer will return a value between 0 and 1.0.
102   *
103   * @param channel The analog input channel this potentiometer is plugged into. 0-3 are on-board
104   *     and 4-7 are on the MXP port.
105   */
106  public AnalogPotentiometer(final int channel) {
107    this(channel, 1, 0);
108  }
109
110  /**
111   * AnalogPotentiometer constructor.
112   *
113   * <p>The potentiometer will return a value between 0 and 1.0.
114   *
115   * @param input The {@link AnalogInput} this potentiometer is plugged into.
116   */
117  public AnalogPotentiometer(final AnalogInput input) {
118    this(input, 1, 0);
119  }
120
121  /**
122   * Get the current reading of the potentiometer.
123   *
124   * @return The current position of the potentiometer.
125   */
126  public double get() {
127    if (m_analogInput == null) {
128      return m_offset;
129    }
130    return (m_analogInput.getVoltage() / RobotController.getVoltage3V3()) * m_fullRange + m_offset;
131  }
132
133  @Override
134  public void initSendable(SendableBuilder builder) {
135    if (m_analogInput != null) {
136      builder.setSmartDashboardType("Analog Input");
137      builder.addDoubleProperty("Value", this::get, null);
138    }
139  }
140
141  @Override
142  public void close() {
143    SendableRegistry.remove(this);
144    if (m_initAnalogInput) {
145      m_analogInput.close();
146      m_analogInput = null;
147      m_initAnalogInput = false;
148    }
149  }
150}