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 SmartIO channel this potentiometer is plugged into.
034   * @param fullRange The scaling to multiply the fraction by to get a meaningful unit.
035   * @param offset The offset to add to the scaled value for controlling the zero value
036   */
037  @SuppressWarnings("this-escape")
038  public AnalogPotentiometer(final int channel, double fullRange, double offset) {
039    this(new AnalogInput(channel), fullRange, offset);
040    m_initAnalogInput = true;
041    SendableRegistry.addChild(this, m_analogInput);
042  }
043
044  /**
045   * AnalogPotentiometer constructor.
046   *
047   * <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
048   * have a 270 degree potentiometer, and you want the output to be degrees with the halfway point
049   * as 0 degrees. The fullRange value is 270.0(degrees) and the offset is -135.0 since the halfway
050   * point after scaling is 135 degrees. This will calculate the result from the fullRange times the
051   * fraction of the supply voltage, plus the offset.
052   *
053   * @param input The {@link AnalogInput} this potentiometer is plugged into.
054   * @param fullRange The angular value (in desired units) representing the full 0-3.3V range of the
055   *     input.
056   * @param offset The angular value (in desired units) representing the angular output at 0V.
057   */
058  @SuppressWarnings("this-escape")
059  public AnalogPotentiometer(final AnalogInput input, double fullRange, double offset) {
060    SendableRegistry.add(this, "AnalogPotentiometer", input.getChannel());
061    m_analogInput = input;
062    m_initAnalogInput = false;
063
064    m_fullRange = fullRange;
065    m_offset = offset;
066  }
067
068  /**
069   * AnalogPotentiometer constructor.
070   *
071   * <p>Use the scale value so that the output produces meaningful values. I.E: you have a 270
072   * degree potentiometer, and you want the output to be degrees with the starting point as 0
073   * degrees. The scale value is 270.0(degrees).
074   *
075   * @param channel The analog input channel this potentiometer is plugged into.
076   * @param scale The scaling to multiply the voltage by to get a meaningful unit.
077   */
078  public AnalogPotentiometer(final int channel, double scale) {
079    this(channel, scale, 0);
080  }
081
082  /**
083   * AnalogPotentiometer constructor.
084   *
085   * <p>Use the fullRange and offset values so that the output produces meaningful values. I.E: you
086   * have a 270 degree potentiometer, and you want the output to be degrees with the starting point
087   * as 0 degrees. The scale value is 270.0(degrees).
088   *
089   * @param input The {@link AnalogInput} this potentiometer is plugged into.
090   * @param scale The scaling to multiply the voltage by to get a meaningful unit.
091   */
092  public AnalogPotentiometer(final AnalogInput input, double scale) {
093    this(input, scale, 0);
094  }
095
096  /**
097   * AnalogPotentiometer constructor.
098   *
099   * <p>The potentiometer will return a value between 0 and 1.0.
100   *
101   * @param channel The analog input channel this potentiometer is plugged into.
102   */
103  public AnalogPotentiometer(final int channel) {
104    this(channel, 1, 0);
105  }
106
107  /**
108   * AnalogPotentiometer constructor.
109   *
110   * <p>The potentiometer will return a value between 0 and 1.0.
111   *
112   * @param input The {@link AnalogInput} this potentiometer is plugged into.
113   */
114  public AnalogPotentiometer(final AnalogInput input) {
115    this(input, 1, 0);
116  }
117
118  /**
119   * Get the current reading of the potentiometer.
120   *
121   * @return The current position of the potentiometer.
122   */
123  public double get() {
124    if (m_analogInput == null) {
125      return m_offset;
126    }
127    return (m_analogInput.getVoltage() / RobotController.getVoltage3V3()) * m_fullRange + m_offset;
128  }
129
130  @Override
131  public void initSendable(SendableBuilder builder) {
132    if (m_analogInput != null) {
133      builder.setSmartDashboardType("Analog Input");
134      builder.addDoubleProperty("Value", this::get, null);
135    }
136  }
137
138  @Override
139  public void close() {
140    SendableRegistry.remove(this);
141    if (m_initAnalogInput) {
142      m_analogInput.close();
143      m_analogInput = null;
144      m_initAnalogInput = false;
145    }
146  }
147}