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 edu.wpi.first.wpilibj;
006
007import edu.wpi.first.hal.FRCNetComm.tResourceType;
008import edu.wpi.first.hal.HAL;
009import edu.wpi.first.util.sendable.SendableBuilder;
010import edu.wpi.first.util.sendable.SendableRegistry;
011
012/**
013 * Standard hobby style servo.
014 *
015 * <p>The range parameters default to the appropriate values for the Hitec HS-322HD servo provided
016 * in the FIRST Kit of Parts in 2008.
017 */
018public class Servo extends PWM {
019  private static final double kMaxServoAngle = 180.0;
020  private static final double kMinServoAngle = 0.0;
021
022  protected static final int kDefaultMaxServoPWM = 2400;
023  protected static final int kDefaultMinServoPWM = 600;
024
025  /**
026   * Constructor.<br>
027   *
028   * <p>By default {@value #kDefaultMaxServoPWM} ms is used as the maxPWM value<br>
029   * By default {@value #kDefaultMinServoPWM} ms is used as the minPWM value<br>
030   *
031   * @param channel The PWM channel to which the servo is attached. 0-9 are on-board, 10-19 are on
032   *     the MXP port
033   */
034  @SuppressWarnings("this-escape")
035  public Servo(final int channel) {
036    super(channel);
037    setBoundsMicroseconds(kDefaultMaxServoPWM, 0, 0, 0, kDefaultMinServoPWM);
038    setPeriodMultiplier(PeriodMultiplier.k4X);
039
040    HAL.report(tResourceType.kResourceType_Servo, getChannel() + 1);
041    SendableRegistry.setName(this, "Servo", getChannel());
042  }
043
044  /**
045   * Set the servo position.
046   *
047   * <p>Servo values range from 0.0 to 1.0 corresponding to the range of full left to full right.
048   *
049   * @param value Position from 0.0 to 1.0.
050   */
051  public void set(double value) {
052    setPosition(value);
053  }
054
055  /**
056   * Get the servo position.
057   *
058   * <p>Servo values range from 0.0 to 1.0 corresponding to the range of full left to full right.
059   * This returns the commanded position, not the position that the servo is actually at, as the
060   * servo does not report its own position.
061   *
062   * @return Position from 0.0 to 1.0.
063   */
064  public double get() {
065    return getPosition();
066  }
067
068  /**
069   * Set the servo angle.
070   *
071   * <p>The angles are based on the HS-322HD Servo, and have a range of 0 to 180 degrees.
072   *
073   * <p>Servo angles that are out of the supported range of the servo simply "saturate" in that
074   * direction In other words, if the servo has a range of (X degrees to Y degrees) than angles of
075   * less than X result in an angle of X being set and angles of more than Y degrees result in an
076   * angle of Y being set.
077   *
078   * @param degrees The angle in degrees to set the servo.
079   */
080  public void setAngle(double degrees) {
081    if (degrees < kMinServoAngle) {
082      degrees = kMinServoAngle;
083    } else if (degrees > kMaxServoAngle) {
084      degrees = kMaxServoAngle;
085    }
086
087    setPosition(((degrees - kMinServoAngle)) / getServoAngleRange());
088  }
089
090  /**
091   * Get the servo angle.
092   *
093   * <p>This returns the commanded angle, not the angle that the servo is actually at, as the servo
094   * does not report its own angle.
095   *
096   * @return The angle in degrees to which the servo is set.
097   */
098  public double getAngle() {
099    return getPosition() * getServoAngleRange() + kMinServoAngle;
100  }
101
102  private double getServoAngleRange() {
103    return kMaxServoAngle - kMinServoAngle;
104  }
105
106  @Override
107  public void initSendable(SendableBuilder builder) {
108    builder.setSmartDashboardType("Servo");
109    builder.addDoubleProperty("Value", this::get, this::set);
110  }
111}