[wpilib] Integrate support for ExpansionHub over USB (#8292)

This commit is contained in:
Thad House
2025-11-01 16:24:58 +00:00
committed by GitHub
parent 4da2511638
commit def7849909
12 changed files with 1875 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/ExpansionHub.h"
#include <memory>
#include <hal/UsageReporting.h>
#include <networktables/BooleanTopic.h>
#include "frc/Errors.h"
#include "frc/ExpansionHubMotor.h"
#include "frc/ExpansionHubServo.h"
#include "frc/SystemServer.h"
using namespace frc;
wpi::mutex ExpansionHub::m_handleLock;
std::weak_ptr<ExpansionHub::DataStore> ExpansionHub::m_storeMap[4];
class ExpansionHub::DataStore {
public:
explicit DataStore(int usbId) : m_usbId{usbId} {
auto systemServer = SystemServer::GetSystemServer();
m_hubConnectedSubscriber =
systemServer.GetBooleanTopic(fmt::format("/rhsp/{}/connected", usbId))
.Subscribe(false);
}
DataStore(DataStore&) = delete;
DataStore(DataStore&&) = delete;
DataStore& operator=(DataStore&) = delete;
DataStore& operator=(DataStore&&) = delete;
nt::BooleanSubscriber m_hubConnectedSubscriber;
uint32_t m_reservedMotorMask{0};
uint32_t m_reservedServoMask{0};
wpi::mutex m_reservedLock;
int m_usbId;
};
std::shared_ptr<ExpansionHub::DataStore> ExpansionHub::GetForUsbId(int usbId) {
FRC_AssertMessage(usbId >= 0 && usbId < NumUsbPorts, "USB {} out of range",
usbId);
std::scoped_lock lock{m_handleLock};
std::weak_ptr<DataStore>& weakStore = m_storeMap[usbId];
auto strongStore = weakStore.lock();
if (!strongStore) {
strongStore = std::make_shared<DataStore>(usbId);
weakStore = strongStore;
}
return strongStore;
}
ExpansionHub::ExpansionHub(int usbId) : m_dataStore{GetForUsbId(usbId)} {}
ExpansionHubServo ExpansionHub::MakeServo(int channel) {
return ExpansionHubServo{m_dataStore->m_usbId, channel};
}
ExpansionHubMotor ExpansionHub::MakeMotor(int channel) {
return ExpansionHubMotor{m_dataStore->m_usbId, channel};
}
bool ExpansionHub::IsHubConnected() const {
return m_dataStore->m_hubConnectedSubscriber.Get(false);
}
bool ExpansionHub::CheckAndReserveServo(int channel) {
int mask = 1 << channel;
std::scoped_lock lock{m_dataStore->m_reservedLock};
if ((m_dataStore->m_reservedServoMask & mask) != 0) {
return false;
}
m_dataStore->m_reservedServoMask |= mask;
return true;
}
void ExpansionHub::UnreserveServo(int channel) {
int mask = 1 << channel;
std::scoped_lock lock{m_dataStore->m_reservedLock};
m_dataStore->m_reservedServoMask &= ~mask;
}
bool ExpansionHub::CheckAndReserveMotor(int channel) {
int mask = 1 << channel;
std::scoped_lock lock{m_dataStore->m_reservedLock};
if ((m_dataStore->m_reservedMotorMask & mask) != 0) {
return false;
}
m_dataStore->m_reservedMotorMask |= mask;
return true;
}
void ExpansionHub::UnreserveMotor(int channel) {
int mask = 1 << channel;
std::scoped_lock lock{m_dataStore->m_reservedLock};
m_dataStore->m_reservedMotorMask &= ~mask;
}
void ExpansionHub::ReportUsage(std::string_view device, std::string_view data) {
HAL_ReportUsage(
fmt::format("ExpansionHub[{}]/{}", m_dataStore->m_usbId, device), data);
}

View File

@@ -0,0 +1,161 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/ExpansionHubMotor.h"
#include "frc/Errors.h"
#include "frc/SystemServer.h"
static constexpr int kPercentageMode = 0;
static constexpr int kVoltageMode = 1;
static constexpr int kPositionMode = 2;
static constexpr int kVelocityMode = 3;
static constexpr int kFollowerMode = 4;
using namespace frc;
ExpansionHubMotor::ExpansionHubMotor(int usbId, int channel)
: m_hub{usbId},
m_channel{channel},
m_velocityPidConstants{usbId, channel, true},
m_positionPidConstants{usbId, channel, false} {
FRC_AssertMessage(channel >= 0 && channel < ExpansionHub::NumMotorPorts,
"ExHub Motor Channel {} out of range", channel);
if (!m_hub.CheckAndReserveMotor(channel)) {
throw FRC_MakeError(err::ResourceAlreadyAllocated, "Channel {}", channel);
}
m_hub.ReportUsage(fmt::format("ExHubServo[{}]", channel), "ExHubServo");
auto systemServer = SystemServer::GetSystemServer();
nt::PubSubOptions options;
options.sendAll = true;
options.keepDuplicates = true;
options.periodic = 0.005;
m_encoderSubscriber = systemServer
.GetDoubleTopic(fmt::format(
"/rhsp/{}/motor{}/encoder", usbId, channel))
.Subscribe(0, options);
m_encoderVelocitySubscriber =
systemServer
.GetDoubleTopic(
fmt::format("/rhsp/{}/motor{}/encoderVelocity", usbId, channel))
.Subscribe(0, options);
m_currentSubscriber = systemServer
.GetDoubleTopic(fmt::format(
"/rhsp/{}/motor{}/current", usbId, channel))
.Subscribe(0, options);
m_setpointPublisher = systemServer
.GetDoubleTopic(fmt::format(
"/rhsp/{}/motor{}/setpoint", usbId, channel))
.Publish(options);
m_distancePerCountPublisher =
systemServer
.GetDoubleTopic(
fmt::format("/rhsp/{}/motor{}/distancePerCount", usbId, channel))
.Publish(options);
m_floatOn0Publisher = systemServer
.GetBooleanTopic(fmt::format(
"/rhsp/{}/motor{}/floatOn0", usbId, channel))
.Publish(options);
m_enabledPublisher = systemServer
.GetBooleanTopic(fmt::format(
"/rhsp/{}/motor{}/enabled", usbId, channel))
.Publish(options);
m_modePublisher =
systemServer
.GetIntegerTopic(fmt::format("/rhsp/{}/motor{}/mode", usbId, channel))
.Publish(options);
m_reversedPublisher = systemServer
.GetBooleanTopic(fmt::format(
"/rhsp/{}/motor{}/reversed", usbId, channel))
.Publish(options);
m_resetEncoderPublisher =
systemServer
.GetBooleanTopic(
fmt::format("/rhsp/{}/motor{}/resetEncoder", usbId, channel))
.Publish(options);
}
ExpansionHubMotor::~ExpansionHubMotor() noexcept {
m_hub.UnreserveMotor(m_channel);
}
void ExpansionHubMotor::SetPercentagePower(double power) {
m_modePublisher.Set(kPercentageMode);
m_setpointPublisher.Set(power);
}
void ExpansionHubMotor::SetVoltage(units::volt_t voltage) {
m_modePublisher.Set(kVoltageMode);
m_setpointPublisher.Set(voltage.to<double>());
}
void ExpansionHubMotor::SetPositionSetpoint(double setpoint) {
m_modePublisher.Set(kPositionMode);
m_setpointPublisher.Set(setpoint);
}
void ExpansionHubMotor::SetVelocitySetpoint(double setpoint) {
m_modePublisher.Set(kVelocityMode);
m_setpointPublisher.Set(setpoint);
}
void ExpansionHubMotor::SetEnabled(bool enabled) {
m_enabledPublisher.Set(enabled);
}
void ExpansionHubMotor::SetFloatOn0(bool floatOn0) {
m_floatOn0Publisher.Set(floatOn0);
}
units::ampere_t ExpansionHubMotor::GetCurrent() const {
return units::ampere_t{m_currentSubscriber.Get(0)};
}
void ExpansionHubMotor::SetDistancePerCount(double perCount) {
m_distancePerCountPublisher.Set(perCount);
}
double ExpansionHubMotor::GetEncoderVelocity() const {
return m_encoderVelocitySubscriber.Get(0);
}
double ExpansionHubMotor::GetEncoderPosition() const {
return m_encoderSubscriber.Get(0);
}
void ExpansionHubMotor::SetReversed(bool reversed) {
m_reversedPublisher.Set(true);
}
void ExpansionHubMotor::ResetEncoder() {
m_resetEncoderPublisher.Set(true);
}
ExpansionHubPidConstants& ExpansionHubMotor::GetVelocityPidConstants() {
return m_velocityPidConstants;
}
ExpansionHubPidConstants& ExpansionHubMotor::GetPositionPidConstants() {
return m_positionPidConstants;
}
void ExpansionHubMotor::Follow(const ExpansionHubMotor& leader) {
if (m_hub.GetUsbId() != leader.m_hub.GetUsbId()) {
throw FRC_MakeError(err::InvalidParameter,
"Cannot follow motor on different hub");
}
m_modePublisher.Set(kFollowerMode);
m_setpointPublisher.Set(leader.m_channel);
}

View File

@@ -0,0 +1,97 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/ExpansionHubPidConstants.h"
#include <string>
#include "frc/Errors.h"
#include "frc/SystemServer.h"
using namespace frc;
ExpansionHubPidConstants::ExpansionHubPidConstants(int usbId, int channel,
bool isVelocityPid) {
auto systemServer = SystemServer::GetSystemServer();
nt::PubSubOptions options;
options.sendAll = true;
options.keepDuplicates = true;
options.periodic = 0.005;
std::string pidType = isVelocityPid ? "velocity" : "position";
m_pPublisher = systemServer
.GetDoubleTopic(fmt::format("/rhsp/{}/motor{}/pid/{}/kp",
usbId, channel, pidType))
.Publish(options);
m_iPublisher = systemServer
.GetDoubleTopic(fmt::format("/rhsp/{}/motor{}/pid/{}/ki",
usbId, channel, pidType))
.Publish(options);
m_dPublisher = systemServer
.GetDoubleTopic(fmt::format("/rhsp/{}/motor{}/pid/{}/kd",
usbId, channel, pidType))
.Publish(options);
m_aPublisher = systemServer
.GetDoubleTopic(fmt::format("/rhsp/{}/motor{}/pid/{}/ka",
usbId, channel, pidType))
.Publish(options);
m_vPublisher = systemServer
.GetDoubleTopic(fmt::format("/rhsp/{}/motor{}/pid/{}/kv",
usbId, channel, pidType))
.Publish(options);
m_sPublisher = systemServer
.GetDoubleTopic(fmt::format("/rhsp/{}/motor{}/pid/{}/ks",
usbId, channel, pidType))
.Publish(options);
m_continuousPublisher =
systemServer
.GetBooleanTopic(fmt::format("/rhsp/{}/motor{}/pid/{}/continous",
usbId, channel, pidType))
.Publish(options);
m_continuousMinimumPublisher =
systemServer
.GetDoubleTopic(
fmt::format("/rhsp/{}/motor{}/pid/{}/continuousMinimum", usbId,
channel, pidType))
.Publish(options);
m_continuousMaximumPublisher =
systemServer
.GetDoubleTopic(
fmt::format("/rhsp/{}/motor{}/pid/{}/continousMaximum", usbId,
channel, pidType))
.Publish(options);
}
void ExpansionHubPidConstants::SetPID(double p, double i, double d) {
m_pPublisher.Set(p);
m_iPublisher.Set(i);
m_dPublisher.Set(d);
}
void ExpansionHubPidConstants::SetFF(double s, double v, double a) {
m_sPublisher.Set(s);
m_vPublisher.Set(v);
m_aPublisher.Set(a);
}
void ExpansionHubPidConstants::EnableContinousInput(double minimumInput,
double maximumInput) {
m_continuousMaximumPublisher.Set(maximumInput);
m_continuousMinimumPublisher.Set(minimumInput);
m_continuousPublisher.Set(true);
}
void ExpansionHubPidConstants::DisableContinousInput() {
m_continuousPublisher.Set(false);
}

View File

@@ -0,0 +1,121 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/ExpansionHubServo.h"
#include "frc/Errors.h"
#include "frc/SystemServer.h"
using namespace frc;
ExpansionHubServo::ExpansionHubServo(int usbId, int channel)
: m_hub{usbId}, m_channel{channel} {
FRC_AssertMessage(channel >= 0 && channel < ExpansionHub::NumServoPorts,
"ExHub Servo Channel {} out of range", channel);
if (!m_hub.CheckAndReserveServo(channel)) {
throw FRC_MakeError(err::ResourceAlreadyAllocated, "Channel {}", channel);
}
m_hub.ReportUsage(fmt::format("ExHubServo[{}]", channel), "ExHubServo");
auto systemServer = SystemServer::GetSystemServer();
nt::PubSubOptions options;
options.sendAll = true;
options.keepDuplicates = true;
options.periodic = 0.005;
m_pulseWidthPublisher =
systemServer
.GetIntegerTopic(
fmt::format("/rhsp/{}/servo{}/pulseWidth", usbId, channel))
.Publish(options);
m_pulseWidthPublisher.Set(1500);
m_framePeriodPublisher =
systemServer
.GetIntegerTopic(
fmt::format("/rhsp/{}/servo{}/framePeriod", usbId, channel))
.Publish(options);
m_framePeriodPublisher.Set(20000);
m_enabledPublisher = systemServer
.GetBooleanTopic(fmt::format(
"/rhsp/{}/servo{}/enabled", usbId, channel))
.Publish(options);
}
ExpansionHubServo::~ExpansionHubServo() noexcept {
m_hub.UnreserveServo(m_channel);
}
void ExpansionHubServo::Set(double value) {
if (m_continousMode) {
value = std::clamp(value, -1.0, 1.0);
value = (value + 1.0) / 2.0;
}
value = std::clamp(value, 0.0, 1.0);
if (m_reversed) {
value = 1.0 - value;
}
auto rawValue = ((value * GetFullRangeScaleFactor()) + m_minPwm);
SetPulseWidth(rawValue);
}
void ExpansionHubServo::SetAngle(units::degree_t angle) {
angle = std::clamp(angle, m_minServoAngle, m_maxServoAngle);
// NOLINTNEXTLINE(bugprone-integer-division)
Set((angle - m_minServoAngle) / GetServoAngleRange());
}
void ExpansionHubServo::SetPulseWidth(units::microsecond_t pulseWidth) {
m_pulseWidthPublisher.Set(pulseWidth.to<double>());
}
void ExpansionHubServo::SetEnabled(bool enabled) {
m_enabledPublisher.Set(enabled);
}
void ExpansionHubServo::SetFramePeriod(units::microsecond_t framePeriod) {
m_framePeriodPublisher.Set(framePeriod.to<double>());
}
units::microsecond_t ExpansionHubServo::GetFullRangeScaleFactor() {
return m_maxPwm - m_minPwm;
}
units::degree_t ExpansionHubServo::GetServoAngleRange() {
return m_maxServoAngle - m_minServoAngle;
}
void ExpansionHubServo::SetPWMRange(units::microsecond_t minPwm,
units::microsecond_t maxPwm) {
if (maxPwm <= minPwm) {
throw FRC_MakeError(err::ParameterOutOfRange,
"Max PWM must be greater than Min PWM");
}
m_minPwm = minPwm;
m_maxPwm = maxPwm;
}
void ExpansionHubServo::SetReversed(bool reversed) {}
void ExpansionHubServo::SetAngleRange(units::degree_t minAngle,
units::degree_t maxAngle) {
if (maxAngle <= minAngle) {
throw FRC_MakeError(err::ParameterOutOfRange,
"Max angle must be greater than Min angle");
}
m_minServoAngle = minAngle;
m_maxServoAngle = maxAngle;
}
void ExpansionHubServo::SetContinousRotationMode(bool enable) {
m_continousMode = enable;
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <memory>
#include <string_view>
#include <wpi/mutex.h>
namespace frc {
class ExpansionHubServo;
class ExpansionHubMotor;
/** This class controls a REV ExpansionHub plugged in over USB to Systemcore. */
class ExpansionHub {
public:
/**
* Constructs a new ExpansionHub for a given USB ID
*
* Multiple instances can be constructed, but will point to the same backing
* object with a ref count.
*
* @param usbId The USB Port ID the hub is plugged into.
*/
explicit ExpansionHub(int usbId);
~ExpansionHub() noexcept = default;
friend class ExpansionHubServo;
friend class ExpansionHubMotor;
/**
* Constructs a servo at the requested channel on this hub.
*
* Only a single instance of each servo per hub can be constructed at a time.
*
* @param channel The servo channel
* @return Servo object
*/
ExpansionHubServo MakeServo(int channel);
/**
* Constructs a motor at the requested channel on this hub.
*
* Only a single instance of each motor per hub can be constructed at a time.
*
* @param channel The motor channel
* @return Motor object
*/
ExpansionHubMotor MakeMotor(int channel);
/**
* Gets if the hub is currently connected over USB.
*
* @return True if hub connection, otherwise false
*/
bool IsHubConnected() const;
/**
* Gets the USB ID of this hub.
*
* @return The USB ID
*/
int GetUsbId() const { return m_usbId; }
static constexpr int NumUsbPorts = 4;
static constexpr int NumServoPorts = 6;
static constexpr int NumMotorPorts = 4;
private:
bool CheckAndReserveServo(int channel);
void UnreserveServo(int channel);
bool CheckAndReserveMotor(int channel);
void UnreserveMotor(int channel);
void ReportUsage(std::string_view device, std::string_view data);
class DataStore;
friend class DataStore;
std::shared_ptr<DataStore> m_dataStore;
int m_usbId;
static wpi::mutex m_handleLock;
static std::weak_ptr<DataStore> m_storeMap[4];
static std::shared_ptr<DataStore> GetForUsbId(int usbId);
};
} // namespace frc

View File

@@ -0,0 +1,174 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <memory>
#include <networktables/BooleanTopic.h>
#include <networktables/DoubleTopic.h>
#include <networktables/IntegerTopic.h>
#include <units/angle.h>
#include <units/current.h>
#include <units/time.h>
#include <units/voltage.h>
#include "frc/ExpansionHub.h"
#include "frc/ExpansionHubPidConstants.h"
namespace frc {
/** This class controls a specific motor and encoder hooked up to an
* ExpansionHub. */
class ExpansionHubMotor {
public:
/**
* Constructs a servo at the requested channel on a specific USB port.
*
* @param usbId The USB port ID the hub is connected to
* @param channel The motor channel
*/
ExpansionHubMotor(int usbId, int channel);
~ExpansionHubMotor() noexcept;
/**
* Sets the percentage power to run the motor at, between -1 and 1.
*
* @param power The power to drive the motor at
*/
void SetPercentagePower(double power);
/**
* Sets the voltage to run the motor at. This value will be continously scaled
* to match the input voltage.
*
* @param voltage The voltage to drive the motor at
*/
void SetVoltage(units::volt_t voltage);
/**
* Command the motor to drive to a specific position setpoint. This value will
* be scaled by SetDistancePerCount and influenced by the PID constants.
*
* @param setpoint The position setpoint to drive the motor to
*/
void SetPositionSetpoint(double setpoint);
/**
* Command the motor to drive to a specific velocity setpoint. This value will
* be scaled by SetDistancePerCount and influenced by the PID constants.
*
* @param setpoint The velocity setpoint to drive the motor to
*/
void SetVelocitySetpoint(double setpoint);
/**
* Sets if the motor output is enabled or not. Defaults to false.
*
* @param enabled True to enable, false to disable
*/
void SetEnabled(bool enabled);
/**
* Sets if the motor should float or brake when 0 is commanded. Defaults to
* false.
*
* @param floatOn0 True to float when commanded 0, false to brake
*/
void SetFloatOn0(bool floatOn0);
/**
* Gets the current being pulled by the motor.
*
* @return Motor current
*/
units::ampere_t GetCurrent() const;
/**
* Sets the distance per count of the encoder. Used to scale encoder readings.
*
* @param perCount The distance moved per count of the encoder
*/
void SetDistancePerCount(double perCount);
/**
* Gets the current velocity of the motor encoder. Scaled into
* distancePerCount units.
*
* @return Encoder velocity
*/
double GetEncoderVelocity() const;
/**
* Gets the current position of the motor encoder. Scaled into
* distancePerCount units.
*
* @return Encoder position
*/
double GetEncoderPosition() const;
/**
* Sets if the motor and encoder should be reversed.
*
* @param reversed True to reverse encoder, false otherwise
*/
void SetReversed(bool reversed);
/** Reset the encoder count to 0. */
void ResetEncoder();
/**
* Gets the PID constants object for velocity PID.
*
* @return Velocity PID constants object
*/
ExpansionHubPidConstants& GetVelocityPidConstants();
/**
* Gets the PID constants object for position PID.
*
* @return Position PID constants object
*/
ExpansionHubPidConstants& GetPositionPidConstants();
/**
* Gets if the underlying ExpansionHub is connected.
*
* @return True if hub is connected, otherwise false
*/
bool IsHubConnected() { return m_hub.IsHubConnected(); }
/**
* Sets this motor to follow another motor on the same hub.
*
* This does not support following motors that are also followers.
* Additionally, the direction of both motors will be the same.
*
* @param leader The motor to follow
*/
void Follow(const ExpansionHubMotor& leader);
private:
ExpansionHub m_hub;
int m_channel;
nt::DoubleSubscriber m_encoderSubscriber;
nt::DoubleSubscriber m_encoderVelocitySubscriber;
nt::DoubleSubscriber m_currentSubscriber;
nt::DoublePublisher m_setpointPublisher;
nt::BooleanPublisher m_floatOn0Publisher;
nt::BooleanPublisher m_enabledPublisher;
nt::IntegerPublisher m_modePublisher;
nt::BooleanPublisher m_reversedPublisher;
nt::BooleanPublisher m_resetEncoderPublisher;
nt::DoublePublisher m_distancePerCountPublisher;
ExpansionHubPidConstants m_velocityPidConstants;
ExpansionHubPidConstants m_positionPidConstants;
};
} // namespace frc

View File

@@ -0,0 +1,81 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <networktables/BooleanTopic.h>
#include <networktables/DoubleTopic.h>
#include <networktables/IntegerTopic.h>
namespace frc {
class ExpansionHubMotor;
/** This class contains PID constants for an ExpansionHub motor. */
class ExpansionHubPidConstants {
public:
/**
* Sets the PID Controller gain parameters.
*
* Sets the proportional, integral, and differential coefficients.
*
* @param p The proportional coefficient. Must be >= 0.
* @param i The integral coefficient. Must be >= 0.
* @param d The differential coefficient. Must be >= 0.
*/
void SetPID(double p, double i, double d);
/**
* Sets the feed forward gains to the specified values.
*
* The units should be radians for angular systems and meters for linear
* systems.
*
* The PID control period is 10ms
*
* @param s The static gain in volts.
* @param v The velocity gain in V/(units/s).
* @param a The acceleration gain in V/(units/s²).
*/
void SetFF(double s, double v, double a);
/**
* Enables continuous input.
*
* Rather then using the max and min input range as constraints, it considers
* them to be the same point and automatically calculates the shortest route
* to the setpoint.
*
* @param minimumInput The minimum value expected from the input.
* @param maximumInput The maximum value expected from the input.
*/
void EnableContinousInput(double minimumInput, double maximumInput);
/**
* Disables continuous input.
*/
void DisableContinousInput();
ExpansionHubPidConstants(ExpansionHubPidConstants&) = delete;
ExpansionHubPidConstants& operator=(ExpansionHubPidConstants&) = delete;
ExpansionHubPidConstants(ExpansionHubPidConstants&&) = default;
ExpansionHubPidConstants& operator=(ExpansionHubPidConstants&&) = default;
friend class ExpansionHubMotor;
private:
ExpansionHubPidConstants(int usbId, int channel, bool isVelocityPid);
nt::DoublePublisher m_pPublisher;
nt::DoublePublisher m_iPublisher;
nt::DoublePublisher m_dPublisher;
nt::DoublePublisher m_sPublisher;
nt::DoublePublisher m_vPublisher;
nt::DoublePublisher m_aPublisher;
nt::BooleanPublisher m_continuousPublisher;
nt::DoublePublisher m_continuousMinimumPublisher;
nt::DoublePublisher m_continuousMaximumPublisher;
};
} // namespace frc

View File

@@ -0,0 +1,140 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <memory>
#include <networktables/BooleanTopic.h>
#include <networktables/IntegerTopic.h>
#include <units/angle.h>
#include <units/time.h>
#include "frc/ExpansionHub.h"
namespace frc {
/** This class controls a specific servo hooked up to an ExpansionHub. */
class ExpansionHubServo {
public:
/**
* Constructs a servo at the requested channel on a specific USB port.
*
* @param usbId The USB port ID the hub is connected to
* @param channel The servo channel
*/
ExpansionHubServo(int usbId, int channel);
~ExpansionHubServo() noexcept;
/**
* Set the servo position.
*
* Servo values range from 0.0 to 1.0 corresponding to the range of full left
* to full right.
*
* @param value Position from 0.0 to 1.0.
*/
void Set(double value);
/**
* Sets the servo angle
*
* Servo angles range from 0 to 180 degrees. Use Set() with your own scaler
* for other angle ranges.
*
* @param angle Position in angle units. Will be scaled between 0 and 180
* degrees
*/
void SetAngle(units::degree_t angle);
/**
* Sets the raw pulse width output on the servo.
*
* @param pulseWidth Pulse width
*/
void SetPulseWidth(units::microsecond_t pulseWidth);
/**
* Sets if the servo output is enabled or not. Defaults to false.
*
* @param enabled True to enable, false to disable
*/
void SetEnabled(bool enabled);
/**
* Sets the frame period for the servo. Defaults to 20ms.
*
* @param framePeriod The frame period
*/
void SetFramePeriod(units::microsecond_t framePeriod);
/**
* Gets if the underlying ExpansionHub is connected.
*
* @return True if hub is connected, otherwise false
*/
bool IsHubConnected() const { return m_hub.IsHubConnected(); }
/**
* Sets the angle range for the setAngle call.
* By default, this is 0 to 180 degrees.
*
* Maximum angle must be greater than minimum angle.
*
* @param minAngle Minimum angle
* @param maxAngle Maximum angle
*/
void SetAngleRange(units::degree_t minAngle, units::degree_t maxAngle);
/**
* Sets the PWM range for the servo.
* By default, this is 600 to 2400 microseconds.
*
* Maximum must be greater than minimum.
*
* @param minPwm Minimum PWM
* @param maxPwm Maximum PWM
*/
void SetPWMRange(units::microsecond_t minPwm, units::microsecond_t maxPwm);
/**
* Sets whether the servo is reversed.
*
* This will reverse both Set() and SetAngle().
*
* @param reversed True to reverse, false for normal
*/
void SetReversed(bool reversed);
/**
* Enables or disables continuous rotation mode.
*
* In continuous rotation mode, the servo will interpret
* Set() commands to between -1.0 and 1.0, instead of 0.0 to 1.0.
*
* @param enable True to enable continuous rotation mode, false to disable
*/
void SetContinousRotationMode(bool enable);
private:
units::microsecond_t GetFullRangeScaleFactor();
units::degree_t GetServoAngleRange();
ExpansionHub m_hub;
int m_channel;
units::degree_t m_maxServoAngle = 180.0_deg;
units::degree_t m_minServoAngle = 0.0_deg;
units::microsecond_t m_minPwm = 600_us;
units::microsecond_t m_maxPwm = 2400_us;
bool m_reversed = false;
bool m_continousMode = false;
nt::IntegerPublisher m_pulseWidthPublisher;
nt::IntegerPublisher m_framePeriodPublisher;
nt::BooleanPublisher m_enabledPublisher;
};
} // namespace frc