Phoenix Pro Documentation#
Welcome to the Phoenix Pro documentation. Individuals looking for Phoenix 5 documentation may locate it here.
The Phoenix Pro software framework allows you to control and configure your CTR Electronics Phoenix Pro Devices. Phoenix Pro represents a complete rewrite of the software framework over the existing Phoenix 5 framework. With Phoenix Pro, users have access to many new features that expand the control the user has over their devices.
Important
User’s looking for documentation on the Phoenix 6 API can find that on the latest version of this site.
For news and updates about your CTR Electronics device, please check out our blog.
Why Phoenix Pro?#
Phoenix Pro currently offers the following features and will further expand.
Comprehensive API#
Device signal getters return a StatusSignalValue object, expanding the functionality of status signals.
Control devices with an extensive list of flexible, strongly-typed control request objects.
Canonical Units#
Uses the popular Units library for C++ and standardizes on SI units.
Signals are documented with the unit type and the minimum and maximum values.
Time Base Synchronization#
Using CANivore Timesync, signals from all devices are sampled and published to the CAN bus at the same time.
API can synchronously wait for data from multiple devices on a CANivore to arrive.
Field Oriented Control (FOC)#
~15% increase in peak power.
Increased torque output; faster acceleration and higher speeds under load.
Greater efficiency; the motor draws less current for the same output power, increasing battery life.
Support for direct torque control.
Improved Device Control#
New and improved control output types and closed-loop configuration.
Improved Motion Magic® with jerk control and support for modifying the profile on the fly.
Kalman-based algorithms to reduce latency while maintaining smooth data.
Fuse a CANcoder with the motor’s internal rotor, getting absolute data all the time while using the fast internal sensor for closed looping.
Enhanced Support for CAN FD#
Improved CAN FD framing further reduces any CAN bus utilization issues.
Larger CAN frames allow for the addition of more advanced features.
New Tuner X Self Tests#
Detailed and resolute self tests to improve debugging.
Free High-Fidelity Simulation#
Simulation closely follows the behavior of real hardware.
Write unit-tests for your robot code, and make sure the robot works before deploying.
Try Phoenix Pro before you buy!
Installing Phoenix Pro#
Installation of Phoenix Pro is comprised of a few steps
API Installation#
Phoenix Pro currently supports Java and C++ for development.
System Requirements#
The following targets are supported:
NI roboRIO
Windows 10/11 x86-64
Linux x86-64 (desktop)
Ubuntu 22.04 or newer
Debian Bullseye or newer
Linux ARM32 and ARM64 (Raspberry Pi, NVIDIA Jetson)
Ubuntu 20.04 or newer
Debian Bullseye or newer
macOS (regular simulation only)
Offline#
Important
Users on non-Windows devices should skip to the Online installation instructions.
Download the Phoenix Framework Installer
Navigate through the installer, ensuring applicable options are selected

Apply the vendordep via WPILib VSCode Adding Offline Libraries
Online#
Important
This vendordep is for robot projects that are only using Phoenix Pro licensed devices.
Paste the following URL in WPILib VS Code Install New Libraries (Online)
https://maven.ctr-electronics.com/release/com/ctre/phoenixpro/PhoenixPro-frc2023-latest.json
Important
This vendordep is for robot projects that are using both Phoenix Pro licensed devices & Phoenix 5 devices.
Paste the following URL in WPILib VS Code Install New Libraries (Online)
https://maven.ctr-electronics.com/release/com/ctre/phoenixpro/PhoenixProAnd5-frc2023-latest.json
Important
Devices on Phoenix Pro firmware must use the Phoenix Pro API. Device on Phoenix 5 firmware must use the Phoenix 5 API.
Phoenix Pro is distributed through our APT repository. Begin with adding the repository to your APT sources.
sudo curl -s --compressed -o /usr/share/keyrings/ctr-pubkey.gpg "https://deb.ctr-electronics.com/ctr-pubkey.gpg"
sudo curl -s --compressed -o /etc/apt/sources.list.d/ctr<year>.list "https://deb.ctr-electronics.com/ctr<year>.list"
Note
<year>
should be replaced with the year of Phoenix Pro software for which you have purchased licenses.
After adding the sources, Phoenix Pro can be installed and updated using the following:
sudo apt update
sudo apt install phoenix-pro
Tip
To get a robot application up and running quickly, check out our non-FRC Linux example.
Tuner X Installation#
Phoenix Tuner X is a modern version of the legacy Phoenix Tuner v1 application that is used to configure CTRE Phoenix CAN devices.
Phoenix Tuner X is supported on Android, Windows 10 (build 1903+), and Windows 11. Installation is available from the respective OS stores.
Configuring your Device#
All CTR Electronics devices have an ID that distinguishes multiple devices of the same type on the same CAN bus. This should be configured to the user’s preference. Firmware upgrading is also generally required for each new release of Phoenix Pro API. Please visit the relevant Tuner pages on how to complete these steps.
Click here to visit the section on updating your device firmware
Click here to visit the section on configuring your device ID
Device Licensing#
The following devices are eligible for single-device licensing:
TalonFX (Falcon 500)
Pigeon 2
CANcoder
Additionally, CANivore is supported for licensing. When a CANivore is licensed, all devices on that bus are Pro enabled without additional activation.
Important
All license activation and verification features are only available in Phoenix Tuner X. Phoenix Tuner v1 does not support licensing actions.
Purchasing a License#
Licenses can be purchased in the licensing section on the CTR Electronics store. Click here to purchase a license.
Once a license has been purchased, you will receive an email confirmation confirming your purchase. Once this email is received, the license should be visible in the list of licenses in Tuner X.
Activating a License#
Licenses are activated by first clicking on the LIC icon in the bottom right corner of the device card.

This will open up a screen which displays a list of currently attached licenses for that device. Click on the Activate a new license button on the bottom of the popup.

A list of purchased (but unattached) license seats are shown here. Click on the license you would like to redeem and press the Activate Selected License button to confirm redemption of that seat.
Warning
Users should be aware that license activation is permanent and irreversible
Once the activation is complete, the license will be downloaded to the device. In the event that Tuner X disconnects from the internet or from the robot before this completes, the license is still activated and available for download the next time Tuner X is connected to the internet/robot.
Activating a License without a Robot#
Devices that have been seen by Tuner X at least once will be available in Device History. This can be useful for licensing a device when disconnected from the robot.
Verifying Activation State#
An icon displaying the license state of your device is located in the bottom right of the device card.

The below table can be used to determine your device license state for troubleshooting.
Troubleshooting#
Did you activate a license for this device?
Clicking on the icon will show licenses that are attached to this device
Is the latest diagnostic server running?
Check the version at the bottom of Tuner X’s devices page.
Latest version details can be found in the changelog under the latest Phoenix-Pro version.
Confirm the vendordep in your robot project is the latest version.
Alternatively, you can deploy the temporary diagnostic server.
Is the latest Pro firmware flashed onto the device?
Phoenix Tuner X#
What is Phoenix Tuner X?#
Note
The legacy Phoenix Tuner v1 is still available for use and is installed as part of the Phoenix installer.
Phoenix Tuner is the companion application allowing you to configure, analyze, update and control your device. Users may choose to use either Phoenix Tuner v1 (included as part of the Phoenix Installer) or Phoenix Tuner X (Android, Windows).
Phoenix Tuner X supports Android 8.0+ and Windows 10 (1903+) and Windows 11.
Important
While CTR Electronics supports both Phoenix Tuner v1 and Phoenix Tuner X, certain features such as device licensing and improved batch upgrading are only available in Phoenix Tuner X.
Tip
Many UI elements contain hover tooltips. That means the user can hover over them with their mouse for a text explanation of what they do.
Connecting Tuner#
Installed onto the robot controller (either manually or via a robot program) is the Phoenix Diagnostics Server. This program enables communication between Tuner X and the robot controller for managing and setting up devices.
Connecting to the Server#
A dropdown/textbox is available in the upper-left flyout menu.

By clicking the arrow, you can change between presets such as:
Driver Station – Retrieves the robot IP from the FRC Driver Station if launched
roboRIO USB – Defaults to
172.22.11.2
which is the roboRIO IP when connected via USBlocalhost – Use for simulation or hardware-attached CANivore.
Alternatively, the user can manually enter the robot IP into the textbox.
Configuring SSH Credentials (non-FRC)#
When using a non-FRC robot controller (non-roboRIO), users must have their SSH credentials configured in Settings for general use.

Temporary Diagnostics (FRC)#
Devices can be configured without a diagnostic server present. This can be useful if the roboRIO has been freshly imaged. Ensure that you are pointed at the roboRIO IP address (usually 10.TE.AM.2
where TE.AM
is the team number) and then click the Run Temporary Diagnostic Server in Settings.

Changing Diagnostics Server Port (non-FRC)#
The target server port can be changed in the Tuner X Settings page, which is accessed from the flyout menu.
Important
The default port for diagnostic server is 1250. FRC users must not change this under any circumstance.

Localhost Troubleshooting#
When Tuner X is first started after installation, it may request admin privileges to access the localhost network. If the user disallows admin access, diagnostic servers hosted on the local machine (simulation and hardware-attached CANivore) may not be visible in Tuner X. Users can manually grant this permission afterward by clicking the Grant Localhost Permissions in Settings.

Device List#
The Devices page is the first page that is shown to the user upon launching the application. The Devices page by default shows a grid of cards, but can be changed to a flat grid view (similar to Phoenix Tuner v1) by clicking on the 4 grid square icon located in the top right corner (not available in Android Tuner X).
Card Colors#
The color of the device cards is helpful as a visual indicator of device state. The meaning of the card color is also shown as text underneath the device title.
Color |
Description |
---|---|
Green |
Device has latest firmware. |
Purple |
Device has unexpected firmware version. |
Yellow |
A new firmware version is available. Check the changelog to determine if the new version matters to your application |
Red |
Device has a duplicate ID. |
Blue |
Failed to retrieve list of available firmware. |
Clipboard Options & Licensing#
Phoenix Tuner X provides icons at the bottom right of each card that will allow the user to copy to the clipboard the device details, configs and Self Test. This can be useful for support requests and additional debugging.

Devices that support CAN FD are shown via a CAN FD icon in the bottom right of the card.
Note
The CAN FD icon does not indicate that the device is currently on a CAN FD bus, merely that it supports CAN FD.
The other major icon in the bottom right of the device card is the licensing indicator. This showcases the licensing states and when clicked, will open the licensing dialog.
Batch Field Upgrade#
Phoenix Tuner X allows the user to batch field upgrade from the Devices page. The user can either select devices by their checkbox (in the top right corner of their respective card) or by selecting the checkmark icon in the top right.
Tip
Selecting a device using their checkbox and clicking the checkmark in the top right will select all devices of the same models

Step 1 in the above image selects all devices of the same model (or all devices if no device is currently check-boxed).
Step 2 in the above image opens the field-upgrade dialog.
Once the upgrade dialog is opened, information detailing the device name, model, ID, and firmware version is presented. There is a Pro column that has a toggle. This toggle represents whether to upgrade to Pro or v5 firmware. If this toggle is disabled (as evident from being greyed out), then there is no available Pro firmware for that device (TalonSRX, legacy devices).

The user can begin the upgrade progress by selecting Update to latest or Select firmware…. The first option will upgrade all listed devices to their latest available firmware (Pro or v5 depending on the toggle state). The second option will open a popup allowing you to select a specific version or firmware file per model.

Tip
Generally, users should update their devices to the latest available firmware version. If manually selecting a CRF is important, the firmware files are available for download on our GitHub Repo.
Important
While the user can cancel firmware upgrading using the “X” button in the top-right, this will not cancel the current device in progress. It will finish upgrading the current device and will not upgrade subsequent devices. Typical Tuner X behavior will resume once the current device finishes flashing.
Device History#
Users can access a list of past devices connected to Tuner X and license them via the Device History page. This is accessible from the left-hand sidebar. This list is not automatically refreshed, but users can refresh it by pressing the refresh icon in the top-right of the page.

Licensing from Device History#
Users can activate a license for a disconnected device by clicking on the device in the Grid. Then, select the “PRO” icon at the bottom right of the device card.

From there, the user can activate a license for the device like normal. Once the device license has been activated, the user still needs to connect Tuner X to the robot to transfer the activated license to the device.
The “PRO” icon may be replaced with a greyed “LIC” icon in the following situations:
The device is on Phoenix 5 firmware AND actively connected to Tuner X
The device is not a Pro compatible device
Users who license an eligible Pro device running Phoenix 5 firmware must update the device firmware to Pro compatible firmware to utilize Pro features.
Device Details#
The Device Details page can be accessed by clicking on the device card (or double clicking on the device row when in grid view). This view allows you to access detailed device actions such as:
Device Details (Name, ID, Firmware Version, Model, Serial No, etc.)
Blinking LEDs
Field Upgrading
Licensing Details (by clicking on the LIC/PRO icon)
Configs
Control
Self Tests
Plotting
Pigeon 2 Mount Calibration
Note
Plotting currently only works with Phoenix 5 devices.
Blinking#
All CTR Electronics devices can be blinked (rapidly flash the LEDs). This can be useful for handling whenever you have duplicate devices using the same ID on the CAN bus.

Verifying Device Details#
This screen highlights information such as (1) Device Name, (2) Device Model, (3) Firmware Version.
Tip
Clicking in the blank space outside the detail frames will bring the user back to the devices page.

Configuring Name & IDs#
All devices can have their Name (1) and ID (2) configured via their respective textbox. IDs are limited to the range of 0 to 62 (inclusive). After inputting the ID or name, press the Set button to save the changes to the device.

Field-Upgrade Firmware Version#
Tuner X has improved firmware upgrading functionality by automatically downloading and caching firmware. Upon initial Tuner X launch, the latest firmware for all devices will automatically be downloaded in the background (takes <10s on most internet connections). The individual device page allows you to select specific firmware versions for your device via the firmware dropdown. Batch firmware can also be completed via the batch field upgrade pop-up.
Important
Users should ensure they select Phoenix Pro firmware when using Phoenix Pro API, and Phoenix 5 firmware when using Phoenix 5 API. A single robot project may use both APIs simultaneously.

Users can switch between “Phoenix Pro” and “Phoenix 5” by clicking on the toggle above the firmware dropdown.
Note
The toggle between Phoenix Pro and Phoenix 5 firmware only affects online field-upgrades.

Tuner Configs#
Tip
Devices can also be configured in code.
Configs can be viewed, modified, backed-up, restored, and factory-reset via the Configs tab in Phoenix Tuner X.

To apply a modified config, press the Apply Configs button on the bottom button bar.

Self Test Snapshot#
Self Test Snapshot is a diagnostic feature of all supported devices that will show the immediate state of the device. This is extremely useful for troubleshooting and ensuring the device is working properly. Phoenix Pro with Phoenix Tuner X improves upon Self Test by showing the information in clean tables, animations and detailed units.

Self Test also includes 3 buttons: Refresh, Blink/Clear Faults and Share to Support. Refresh will refresh the Self Test information, Blink/Clear faults` will blink the device and clear any faults on the device. Share to Support will open the default email client with an email to CTR Electronics support.
Viewing Status LEDs#
Phoenix Pro devices report status LEDs as an animated GIF in Phoenix Tuner X. This can be useful for diagnosing a device when it’s buried in a robot.

Plotting#
Supported devices can have certain signals/sensor data plotted in real-time without any additional configuration. To get started, click on the Plot button in the top right navigation bar.
Tip
Plotting is supported in both Phoenix 5 and Phoenix Pro.

At the top of this page is a list of supported values that can then be plotted. Click on the signal that you wish to plot. Then click Enable Plot on the left.

Adjusting Plotting Time Period#
Plotting time period (the time frame that points are recorded) can be adjusted using the Time Period textbox.

Exporting Data#
Plots can be exported into csv format for viewing in an external analysis tool. Click on the Export as CSV button.
Plot Appearance & Behavior#
Important
Scatter points may dramatically affect Tuner X performance.
Plotting supports zoom and panning via the mouse and scroll wheel (or via gestures on Android). The point appearance can also be adjusted between “Spline” and “Scatter”.

Pigeon 2.0 Calibration#
It is recommended that calibration is performed once the Pigeon 2.0 has been mounted to the robot. Calibration will calculate the optimal offsets to apply to ensure that Pose, Pitch and Yaw is 0 when the robot is considered “flat”. Users can access the calibration menu by clicking on the Pigeon 2.0 in Devices and clicking Calibration in the top right.

Read through the on-screen instructions and click Begin Mount Calibration.

TalonFX#
The Falcon 500 powered by Talon FX is a brushless motor with an integrated motor controller and high-resolution encoder, custom designed specifically for the FIRST Robotics Competition, through a collaboration between Cross the Road Electronics and VEX Robotics.
Actuator Limits#
CTR Electronics actuators, such as the TalonFX, support various kinds of hardware and software limits.
Documentation on retrieving and configuring limits can be found here.
Limit Switches#
CTR Electronics supported actuators have limit features that will automatically neutral the actuator output (set voltage to 0) if a limit switch is activated. By default, limits are set to “normally open”. This means that the switch needs to be explicitly closed (or grounded) for the actuator output to be set to neutral.
When the limit switch is closed (connected to ground), the actuator will disable and the pattern will move toward the forward/reverse limit pin (red blink pattern will move toward the forward limit pin when the forward limit is closed, and vice-versa).
Tip
For more information on limit switch wiring, consult the TalonFX User’s Guide.
Status Light Reference#

LED State |
Description |
---|---|
Alternating Off/Orange |
Talon FX is disabled. Robot controller is missing on the bus or the diagnostic server is not installed. |
Simultaneous Off/Orange |
Talon FX is disabled. Phoenix is running in Robot Controller. |
Alternating Red/Green |
Talon FX is not licensed. Please license device in Phoenix Tuner. |
Off/Slow Red |
CAN/PWM is not detected. |
Red/Orange |
Damaged Hardware |
Off/Red |
Limit Switch or Soft Limit triggered. |
Green/Orange |
Device is in bootloader. |
Pigeon 2.0#
Pigeon 2.0 is the next evolution in the family of Pigeon IMUs.
With no on-boot calibration or temperature calibration required and dramatic improvement to drift, the Pigeon is the easiest IMU to use yet.
Pigeon 2 Troubleshooting#
A functional limitation was discovered in Pigeon 2s manufactured in September of 2022. When used on a CANivore (CAN FD) Bus, the Pigeon 2 may not transmit CAN FD frames correctly. As a result, you may find that all CAN device LEDs go red when the Pigeon 2 is in-circuit and powered.
A firmware fix has been published, to update the firmware of an affected Pigeon 2, one of the below options can be used.
Option 1: Workaround with Tuner X#
Note
If you do not see the below option, then Tuner X is likely older than version 2023.1.5.0.
A new section in Tuner X Settings labeled Pigeon 2 Workaround has been added. When the Execute Pigeon 2 workaround button is pressed, all CANivores will enter a special mode that allows them to see the offending Pigeon 2s. This mode is reverted when the CANivore is power cycled.

Once the workaround has been applied, the device will show up in the Devices menu and the LED should be alternating green/orange. Field-upgrade the firmware version and power cycle the CANivore.
Option 2: Connect to the roboRIO Bus#
Connect the Pigeon 2 to the roboRIO CAN Bus and field-upgrade the firmware version.
Note
We recommend power cycling Pigeon after moving CAN bus leads from CANivore to roboRIO CAN bus to ensure a clean transition.
Status Light Reference#

LED Color |
Blink Pattern |
Description |
---|---|---|
Off |
Pigeon 2.0 is not powered. |
|
Yellow/Green |
Only a single LED will blink with this pattern. |
Device is in boot-loader, most likely because firmware upgrading has failed. Inspect CAN bus wiring and retry firmware upgrading. If device has valid firmware, turn device off, wait 10 seconds, and turn device back on. |
Red/Green |
Alternating Red/Green |
Device is not licensed. License device in Phoenix Tuner. |
Red/Yellow |
LEDs are never off - one of the two colors are always illuminated |
Hardware is damaged |
Red Blink |
Check CAN bus health and connection to the Pigeon 2.0 |
|
Yellow |
Alternate Blinking |
CAN bus detected but robot controller is not detected (or Pigeon 2.0 is not referenced in code) |
Yellow |
Simultaneous Blinking |
CAN bus detected, robot is disabled. |
Green Blink |
CAN bus detected. Robot is enabled |
Mount Calibration#
It’s recommended to perform a mount calibration when placement of the Pigeon 2.0 has been finalized. This can be done via the Calibration page in Tuner X.

CANcoder#
Important
As of late August 2022, there are multiple hardware versions of CANcoder available. This is due to the ongoing worldwide chip shortage causing CTR Electronics to replace the original processor with a substitute. This new version of CANcoder requires a different firmware, but is otherwise functionally identical to the original. Details on checking the version can be found in the device details section.
The CANcoder is the next evolution in the line of CTRE magnetic encoder products. As its name implies, this product is a rotary magnetic encoder that communicates over the CAN bus. Supporting CAN FD and CAN 2.0, this product provides the same position and velocity with the same resolutions you’ve come to expect from the SRX Magnetic Encoder.
Status Light Reference#
Note
Users wishing to test magnet placement must wait 8 seconds after boot for the LEDs to blink the magnet placement status.
LED Color |
Led Brightness |
CAN bus Detection |
Magnet Field Strength |
Description |
---|---|---|---|---|
Off |
CANcoder is not powered/plugged in. Check power cabling to the CANcoder. |
|||
Yellow/Green |
Bright |
Device is in boot-loader, most likely because firmware upgrading has failed. Inspect CAN bus wiring and retry firmware upgrading. If device has valid firmware, turn device off, wait 10 seconds, and turn device back on. |
||
Slow Red Blink |
Bright |
CAN bus has been lost. |
Check CAN bus health and connection to the CANcoder. |
|
Red/Green |
Bright |
Device is not licensed. Please license device in Phoenix Tuner. |
||
Rapid Red Blink |
Dim |
CAN bus never detected since boot |
<25mT or >135mT |
Magnet is out of range |
Rapid Yellow Blink |
Dim |
CAN bus never detected since boot |
25-45mT or 75-136mT |
Magnet in range with slightly reduced accuracy |
Rapid Green Blink |
Dim |
CAN bus never detected since boot |
Magnet in range |
Magnet in range |
Rapid Red Blink |
Bright |
CAN bus present |
<25mT or >135mT |
Magnet is out of range |
Rapid Yellow Blink |
Bright |
CAN bus present |
25-45mT or 75-136mT |
Magnet in range with slightly reduced accuracy |
Rapid Green Blink |
Bright |
CAN bus present |
Magnet in range |
Magnet in range |
Magnet Placement#
Using the CANcoder User’s Guide, verify that magnet placement is correct for the CANcoder.
Verifying Sensor Direction#
CANcoder sensor direction can be configured via the Config page in Phoenix Tuner X.

API Usage#
This section serves to provide basic API usage for the Phoenix Pro API. For full details, please visit the API docs (Java, C++).
Important
While Phoenix Pro and Phoenix 5 devices may exist on the same CAN bus and same robot project, each robot project must use the API tied to the device firmware version. This means Phoenix 5 devices must use the Phoenix 5 API, and Phoenix Pro devices must use the Phoenix Pro API.
There are three major components to the Phoenix Pro API:
- API Overview
Details a high level overview of what makes up the Phoenix Pro API.
- Configuration
Describes configuring device configs via code.
- Control Requests
Highlights using control requests to control the open and closed loop functionality of actuators such as the TalonFX.
- Status Signals
Details using status signals to retrieve sensor data from devices.
- Device Faults
Documents how faults are used to indicate device hardware status.
- Enabling Actuators
Information on the FRC Lock safety feature and enabling actuators.
- Actuator Limits
Documents how to retrieve and configure software and hardware actuator limits.
- Device Specific
Describes some device specific functionality, such as the different TalonFX control requests and how they are used.
- Migration Guide
A “cheat sheet” on migrating from Phoenix 5 to Phoenix Pro.
API Overview#
The Phoenix Pro API resides in the com.ctre.phoenixpro
package in Java and the ctre::phoenixpro
namespace in C++. The API is then further organized into smaller packages and namespaces that group together similar types of classes and functions:
configs
- classes related to device configurationcontrols
- classes related to device controlhardware
- the device hardware classes, such asTalonFX
signals
- enumeration types for device signalssim
- classes related to device simulation
C++ IntelliSense#
In C++, this namespace structure has the advantage of cleaning up IntelliSense when searching for classes:
// first use the ctre::phoenixpro namespace
using namespace ctre::phoenixpro;
// now types are organized cleanly by namespace
hardware::TalonFX m_talonFX{0};
sim::TalonFXSimState& m_talonFXSim{m_talonFX.GetSimState()};
configs::TalonFXConfiguration m_talonFXConfig{};
signals::InvertedValue m_talonFXInverted{signals::InvertedValue::CounterClockwise_Positive};
controls::DutyCycleOut m_talonFXOut{0};
All C++ code examples in this documentation will assume the presence of using namespace ctre::phoenixpro;
.
Configuration#
Devices support persistent settings through the use of “configs”.
Tip
Configs can also be configured using Phoenix Tuner X. See Tuner Configs for more information.
Configuration Objects#
There are device-specific Configuration
classes that group configuration data of devices in a meaningful way.
These classes are Passive Data Structures.
One example is TalonFXConfiguration
, which has subgroups of configs such as MotorOutputConfigs
.
The configs can be modified through public member variables of the Configuration
object.
The complete list of configuration objects can be found in the API documentation (Java, C++).
var talonFXConfigs = new TalonFXConfiguration();
configs::TalonFXConfiguration talonFXConfigs{};
Future Proofing Configs#
There is a corner case with configs where the device may have firmware with newer configs that didn’t exist when the version of the API was built. To account for this problem, device Configuration
objects have a FutureProofConfigs
(Java, C++) field.
Configurator API#
Device objects have a getConfigurator()
method that returns a device-specific Configurator
object.
The Configurator
is used to retrieve, apply, and factory default the configs of a device.
Note
This routine can be called frequently without any performance implications.
The device-specific configurators have type-specific overloads that allow for the widest variety of device-compatible configs.
As a result, the caller can pass the entire device Configuration
object or just the relevant subgroup of configs to the Configurator
API.
var talonFXConfigurator = m_talonFX.getConfigurator();
auto& talonFXConfigurator = m_talonFX.GetConfigurator();
Reading Configs#
To read configs stored in a device, use the refresh()
method to update a Configuration
object. The example below demonstrates retrieving a full TalonFXConfiguration
(Java, C++) object from a TalonFX
device.
var talonFXConfigurator = m_talonFX.getConfigurator();
var talonFXConfigs = new TalonFXConfiguration();
// optional timeout (in seconds) as a second optional parameter
talonFXConfigurator.refresh(talonFXConfigs);
auto& talonFXConfigurator = m_talonFX.GetConfigurator();
configs::TalonFXConfiguration talonFXConfigs{};
// optional timeout (in seconds) as a second optional parameter
talonFXConfigurator.Refresh(talonFXConfigs);
Applying Configs#
Configs can be applied to a device by calling apply()
on the Configurator
with a Configuration
object.
var talonFXConfigurator = m_talonFX.getConfigurator();
var motorConfigs = new MotorOutputConfigs();
// set invert to CW+ and apply config change
motorConfigs.Inverted = InvertedValue.Clockwise_Positive;
talonFXConfigurator.apply(motorConfigs);
auto& talonFXConfigurator = m_talonFX.GetConfigurator();
configs::MotorOutputConfigs motorConfigs{};
// set invert to CW+ and apply config change
motorConfigs.Inverted = signals::InvertedValue::Clockwise_Positive;
talonFXConfigurator.Apply(motorConfigs);
Tip
To modify a single configuration value without affecting the other configs, users can call refresh()
after constructing the config object, or users can cache the config object and reuse it for future calls to apply()
.
Factory Default#
A newly-created Configuration
object contains the default configuration values of a device.
Passing this newly-created Configuration
object to the device Configurator
will factory default the device’s configs.
m_talonFX.getConfigurator().apply(new TalonFXConfiguration());
m_talonFX.GetConfigurator().Apply(configs::TalonFXConfiguration{});
Control Requests#
Control Requests represent the output of a device. A list of control requests can be found in the API docs (Java, C++).
Note
Phoenix Pro utilizes the C++ units library when applicable.
Applying a Control Request#
Control requests can be applied by calling setControl()
on the motor object. setControl()
returns a StatusCode
(Java, C++) enum that represents success state. A successful request will return StatusCode.OK
.
// Command m_motor to 100% of duty cycle
m_motor.setControl(new DutyCycleOut(1.0));
// Command m_motor to 100% of duty cycle
m_motor.SetControl(controls::DutyCycleOut{1.0});
Modifying a Control Request#
Control requests are mutable, so they can be saved in a member variable and reused. For example, DutyCycleOut
(Java, C++) has an Output
member variable that can be manipulated, thus changing the output DutyCycle (proportion of supply voltage).
Note
Java users should reuse control requests to prevent excessive invocation of the Garbage Collector.
var motorRequest = new DutyCycleOut(0.0);
motorRequest.Output = 1.0;
m_motor.setControl(motorRequest);
controls::DutyCycleOut motorRequest{0.0};
motorRequest.Output = 1.0;
m_motor.SetControl(motorRequest);
Method Chaining API#
Control requests also supports modification using method chaining. This can be useful for mutating multiple values of a control request.
// initialize torque current FOC request with 0 amps
var motorRequest = new TorqueCurrentFOC(0);
// mutate request with output of 10 amps and max duty cycle 0.5
m_motor.SetControl(motorRequest.withOutputAmps(10).withMaxDutyCycle(0.5));
// initialize torque current FOC request with 0 amps
controls::TorqueCurrentFOC motorRequest{0_A};
// mutate request with output of 10 amps and max duty cycle 0.5
m_motor.SetControl(motorRequest.WithOutputAmps(10_A).WithMaxDutyCycle(0.5));
Changing Update Frequency#
Control requests are automatically transmitted at a fixed update frequency. This update frequency can be modified by changing the UpdateFreqHz
(Java, C++) field of the control request before sending it to the device.
// create a duty cycle request
var motorRequest = new DutyCycleOut(0);
// reduce the update frequency to 50 Hz
motorRequest.UpdateFreqHz = 50;
// create a duty cycle request
controls::DutyCycleOut motorRequest{0};
// reduce the update frequency to 50 Hz
motorRequest.UpdateFreqHz = 50;
Status Signals#
Signals represent live data reported by a device; these can be yaw, position, etc. To make use of the live data, users need to know the value, timestamp, latency, units, and error condition of the data. Additionally, users may need to synchronize with fresh data to minimize latency.
StatusSignalValue
#
The StatusSignalValue
(Java, C++) is a signal object that provides APIs to address all of the requirements listed above.
The device object provides getters for all available signals. Each getter returns a StatusSignalValue
that is typed appropriately for the signal.
Note
The device getters return a cached StatusSignalValue
. As a result, frequently calling the getter does not influence RAM performance.
var supplyVoltageSignal = m_device.getSupplyVoltage();
auto& supplyVoltageSignal = m_device.GetSupplyVoltage();
The value of the signal can be retrieved from the StatusSignalValue
by calling getValue()
.
var supplyVoltage = supplyVoltageSignal.getValue();
auto supplyVoltage = supplyVoltageSignal.GetValue();
Note
Phoenix Pro utilizes the C++ units library when applicable.
The StatusCode
(Java, C++) of the signal can be retrieved by calling getError()
.
This can be used to determine if the device is not present on the CAN bus.
Note
If a status signal is not available on the CAN bus, an error will be reported to the Driver Station.
Refreshing the Signal Value#
The device StatusSignalValue
getters implicitly refresh the cached signal values. However, if the user application caches the StatusSignalValue
object, the refresh()
method must be called to fetch fresh data.
Tip
The refresh()
method can be method-chained. As a result, you can call refresh()
and getValue()
on one line.
supplyVoltageSignal.refresh();
supplyVoltageSignal.Refresh();
Waiting for Signal Updates#
Instead of using the latest value, the user can instead opt to synchronously wait for a signal update. StatusSignalValue
provides a waitForUpdate(timeoutSec)
method that will block the current robot loop until the signal is retrieved or the timeout has been exceeded. This replaces the need to call refresh()
on cached StatusSignalValue
objects.
Tip
If you want to zero your sensors, you can use this API to ensure the set operation has completed before continuing program flow.
Tip
The waitForUpdate()
method can be method-chained. As a result, you can call waitForUpdate()
and getValue()
on one line.
// wait up to 1 robot loop iteration (20ms) for fresh data
supplyVoltageSignal.waitForUpdate(0.020);
// wait up to 1 robot loop iteration (20ms) for fresh data
supplyVoltageSignal.WaitForUpdate(20_ms);
Changing Update Frequency#
All signals can have their update frequency configured via the setUpdateFrequency()
method.
Warning
Increasing signal frequency will also increase CAN bus utilization, which can cause indeterminate behavior at high utilization rates (>90%). This is less of a concern when using CANivore, which uses the higher-bandwidth CAN FD bus.
// slow down supply voltage reporting to 10 Hz
supplyVoltageSignal.setUpdateFrequency(10);
// slow down supply voltage reporting to 10 Hz
supplyVoltageSignal.SetUpdateFrequency(10_Hz);
Timestamps#
The timestamps of a StatusSignalValue
can be retrieved by calling getAllTimestamps()
, which returns a collection of Timestamp
(Java, C++) objects. The Timestamp
objects can be used to perform latency compensation math.
CANivore Timesync#
When using CANivore, the attached CAN devices will automatically synchronize their time bases. This allows devices to sample and publish their signals in a synchronized manner.
Users can synchronously wait for these signals to update using BaseStatusSignalValue.waitForAll()
(Java, C++).
Tip
waitForAll()
can be used with a timeout of zero to perform a non-blocking refresh on all signals passed in.
Because the devices are synchronized, time-critical signals are sampled and published on the same schedule. This combined with the waitForAll()
routine means applications can considerably reduce the latency of the timesync signals. This is particularly useful for multi-device mechanisms, such as swerve odometry.
Note
When using a non-zero timeout, the signals passed into waitForAll()
should have the same update frequency for synchronous data acquisition. This can be done by calling setUpdateFrequency()
or by referring to the API documentation.
The following signals are time-synchronized:
TalonFX
All Signals
CANcoder
All Signals
Pigeon 2.0
Yaw, Pitch, & Roll
Quaternion
Gravity Vector
Accum Gyro
Angular Rate
Accelerometer
Temperature
var talonFXPositionSignal = m_talonFX.getPosition();
var cancoderPositionSignal = m_cancoder.getPosition();
var pigeon2YawSignal = m_pigeon2.getYaw();
BaseStatusSignalValue.waitForAll(0.020, talonFXPositionSignal, cancoderPositionSignal, pigeon2YawSignal);
auto& talonFXPositionSignal = m_talonFX.GetPosition();
auto& cancoderPositionSignal = m_cancoder.GetPosition();
auto& pigeon2YawSignal = m_pigeon2.GetYaw();
BaseStatusSignalValue::WaitForAll(20_ms, {&talonFXPositionSignal, &cancoderPositionSignal, &pigeon2YawSignal});
Latency Compensation#
Users can perform latency compensation using BaseStatusSignalValue.getLatencyCompensatedValue()
(Java, C++).
Important
getLatencyCompensatedValue()
does not automatically refresh the signals. As a result, the user must ensure the signal
and signalSlope
parameters are refreshed before retrieving a compensated value.
double compensatedTurns = BaseStatusSignalValue.getLatencyCompensatedValue(m_motor.getPosition(), m_motor.getVelocity());
auto compensatedTurns = BaseStatusSignalValue::GetLatencyCompensatedValue(m_motor.GetPosition(), m_motor.GetVelocity());
SignalMeasurement
#
All StatusSignalValue
objects have a getDataCopy()
method that returns a new SignalMeasurement
(Java, C++) object. SignalMeasurement
is a Passive Data Structure that provides all the information about a signal at the time of the getDataCopy()
call, which can be useful for data logging.
Warning
getDataCopy()
returns a new SignalMeasurement
object every call. Java users should avoid using this API in RAM-constrained applications.
Device Faults#
“Faults” are status indicators on CTR Electronics CAN devices that indicate a certain behavior or event has occurred. Faults do not directly affect the behavior of a device; instead, they indicate the device’s current status and highlight potential issues.
Faults are stored in two fashions. There are “live” faults, which are reported in real-time, and “sticky” faults, which assert persistently and stay asserted until they are manually cleared (like trouble codes in a vehicle).
Sticky Faults can be cleared by clicking the Blink/Clear Faults button in Phoenix Tuner X, or by calling clearStickyFaults()
on the device in the robot program. A regular fault can only be cleared when the offending problem has been resolved.

Using API to Retrieve Faults#
Faults can also be retrieved in API using the getFault_*()
(regular) or getStickyFault_*()
(sticky) methods on the device object. This can be useful for diagnostics or error handling.
var faulted = m_cancoder.getFault_BadMagnet().getValue();
if (faulted) {
// do action when bad magnet fault is set
}
auto faulted = m_cancoder.GetFault_BadMagnet().GetValue();
if (faulted) {
// do action when bad magnet fault is set
}
A list of possible faults can be found in the API documentation for each device.
Enabling Actuators#
CTR Electronics supported actuators have a safety feature where they will automatically disable output if they have not recently received an enable signal.
FRC Applications#
In FRC applications, the enable signal is automatically sent to devices based on the Driver Station enable signal. This includes controlling devices in Phoenix Tuner X.
Warning
The device FRC Lock must be cleared to control devices in hardware-attached simulation.
Non-FRC Applications#
In non-FRC applications, Unmanaged.feedEnable()
must be called periodically to enable actuators.
Warning
The device FRC Lock must be cleared to control devices.
// feed the enable signal, timeout after 100ms
Unmanaged.feedEnable(100);
// feed the enable signal, timeout after 100ms
unmanaged::FeedEnable(100);
This must also be called to control devices in Phoenix Tuner X.
Tip
The Tuner X CANivore USB server automatically calls Unmanaged.feedEnable()
when control is enabled.
FRC Lock#
When a device is connected to a roboRIO for use in FRC, the device becomes FRC-locked and will require the Driver Station enable signal for actuation. The device FRC lock can be reset by factory-defaulting the device in Phoenix Tuner X.
Actuator Limits#
CTR Electronics actuators, such as the TalonFX, support various kinds of hardware and software limits.
Documentation on wiring limit switches can be found here.
Retrieving Limit Switch State#
The state of the forward or reverse limit switch can be retrieved from the API via getForwardLimit()
and getReverseLimit()
.
var forwardLimit = m_motor.getForwardLimit();
if (forwardLimit.getValue() == ForwardLimitValue.ClosedToGround) {
// do action when forward limit is closed
}
auto& forwardLimit = m_motor.GetForwardLimit();
if (forwardLimit.GetValue() == signals::ForwardLimitValue::ClosedToGround) {
// do action when forward limit is closed
}
Device Specific#
This section is intended to highlight any device specific API functionality. This include features such as the TalonFX + CANcoder fusion, details on using TalonFX Control Requests and more.
TalonFX#
Introduction to TalonFX Control#
The TalonFX has a variety of open-loop and closed-loop control requests and supports Field Oriented Control.
Control Output Types#
The TalonFX supports three base control output types: DutyCycle, Voltage, and TorqueCurrentFOC.
A DutyCycle control request outputs a proportion of the supply voltage, which typically ranges from -1.0 to 1.0, inclusive. This control output type is typically used in systems where it is important to be capable of running at the maximum speed possible, such as in a typical robot drivetrain.
A Voltage control request directly controls the output voltage of the motor. The output voltage is capped by the supply voltage to the device. Since the output of a Voltage control request is typically unaffected by the supply voltage, this control output type results in more stable and reproducible behavior than a DutyCycle control request.
A TorqueCurrentFOC control request uses Field Oriented Control to directly control the output current of the motor. Unlike the other control output types, where output roughly controls the velocity of the motor, a TorqueCurrentFOC request controls the acceleration of the motor.
Field Oriented Control#
Field Oriented Control (FOC) is a commutation mode that increases peak power by ~15%. All control modes that optionally support FOC have an EnableFOC
field (Java, C++). There are also control types that require FOC, such as TorqueCurrentFOC.
Open-Loop Control#
Open-Loop control typically refers to directly controlling device output.
There are open-loop control requests for all TalonFX control output types. With the exception of FOC-only control requests, all open-loop control requests follow the naming pattern {ControlOutputType}Out
. For example, the open-loop Voltage control request is called VoltageOut
. FOC-only control requests follow the naming pattern {ControlOutputType}
.
// users should reuse control requests when possible
var leftRequest = new DutyCycleOut(0.0);
var rightRequest = new DutyCycleOut(0.0);
// retrieve joystick inputs
var forward = -m_driverJoy.getLeftY();
var turn = m_driverJoy.getRightX();
// calculate motor outputs, utilizes a "arcade" style of driving;
// where left Y controls forward and right X controls rotation/turn
var leftOut = forward + turn;
var rightOut = forward - turn;
// set request to motor controller
m_leftLeader.setControl(leftRequest.withOutput(leftOut));
m_rightLeader.setControl(rightRequest.withOutput(rightOut));
// users should reuse control requests when possible
controls::DutyCycleOut leftRequest{0.0};
controls::DutyCycleOut rightRequest{0.0};
// retrieve joystick inputs
auto forward = -m_driverJoy.GetLeftY();
auto turn = m_driverJoy.GetRightX();
// calculate motor outputs, utilizes a "arcade" style of driving;
// where left Y controls forward and right X controls rotation/turn
auto leftOut = forward + turn;
auto rightOut = forward - turn;
// set request to motor controller
m_leftLeader.SetControl(leftRequest.WithOutput(leftOut));
m_rightLeader.SetControl(rightRequest.WithOutput(rightOut));
Closed-Loop Control#
Closed-loop control typically refers to control of a motor that relies on sensor data to adjust based on error. Systems/mechanisms that rely on maintaining a certain position or velocity achieve this state using closed-loop control. This is achieved by feedback (PID) and feedforward control. Closed-loop control can be performed on the robot controller or on the individual motor controllers. The benefit of onboard closed-loop control is that there is no sensor latency and 1 kHz update frequency. This can result in a more responsive output compared to running the closed-loop on the robot controller.
Since closed-loop control changes based on the dynamics of the system (velocity, mass, CoG, etc.), closed-loop relies on PID and feedforward parameters. These parameters are configured either via Tuner Configs or in code. The parameters can be determined using System Identification (such as with WPILib SysID) or through manual tuning.
Manual tuning typically follows this process:
Set \(K_p\), \(K_i\) and \(K_d\) to zero.
Increase \(K_p\) until the output starts to oscillate around the setpoint.
Increase \(K_d\) as much as possible without introducing jittering to the response.
All closed-loop control requests follow the naming pattern {ClosedLoopMode}{ControlOutputType}
. For example, the VelocityVoltage
control request performs a velocity closed-loop using voltage output.
Gain Slots#
It may be useful to switch between presets of gains in a motor controller, so the TalonFX supports multiple gain slots. All closed-loop control requests have a member variable Slot
that can be assigned an integer ID to select the set of gains used by the closed-loop. The gain slots can be configured in code using Slot*Configs
(Java, C++) objects.
Velocity Control#
A Velocity closed loop can be used to maintain a target velocity (in rotations per second). This can be useful for controlling flywheels, where a velocity needs to be maintained for accurate shooting.
Velocity closed loop is currently supported for all base control output types. The units of the output is determined by the control output type.
In a Velocity closed loop, the gains should be configured as follows:
\(K_s\) - output to overcome static friction (output)
\(K_v\) - output per unit of requested velocity (output/rps)
\(K_p\) - output per unit of error in velocity (output/rps)
\(K_i\) - output per unit of integrated error in velocity (output/rotation)
\(K_d\) - output per unit of error derivative in velocity (output/(rps/s))
// in init function, set slot 0 gains
var slot0Configs = new Slot0Configs();
slot0Configs.kS = 0.05; // Add 0.05 V output to overcome static friction
slot0Configs.kV = 0.12; // A velocity target of 1 rps results in 0.12 V output
slot0Configs.kP = 0.11; // An error of 1 rps results in 0.11 V output
slot0Configs.kI = 0.5; // An error of 1 rps increases output by 0.5 V each second
slot0Configs.kD = 0.01; // An acceleration of 1 rps/s results in 0.01 V output
m_talonFX.getConfigurator().apply(slot0Configs);
// in init function, set slot 0 gains
configs::Slot0Configs slot0Configs{};
slot0Configs.kS = 0.05; // Add 0.05 V output to overcome static friction
slot0Configs.kV = 0.12; // A velocity target of 1 rps results in 0.12 V output
slot0Configs.kP = 0.11; // An error of 1 rps results in 0.11 V output
slot0Configs.kI = 0.5; // An error of 1 rps increases output by 0.5 V each second
slot0Configs.kD = 0.01; // An acceleration of 1 rps/s results in 0.01 V output
m_talonFX.GetConfigurator().Apply(slot0Configs);
Once the gains are configured, the Velocity closed loop control request can be sent to the TalonFX. The control request object has an optional feedforward term that can be used to add an arbitrary value to the output, which can be useful to account for the effects of gravity.
// create a velocity closed-loop request, voltage output, slot 0 configs
var request = new VelocityVoltage(0).withSlot(0);
// set velocity to 8 rps, add 0.5 V to overcome gravity
m_talonFX.setControl(request.withVelocity(8).withFeedForward(0.5));
// create a velocity closed-loop request, voltage output, slot 0 configs
auto request = controls::VelocityVoltage{0_tps}.WithSlot(0);
// set velocity to 8 rps, add 0.5 V to overcome gravity
m_talonFX.SetControl(request.WithVelocity(8_tps).WithFeedForward(0.5_V));
In some applications, it may be useful to translate between meters and rotations. This can be done using the following equation:
where meters
is the target in meters, wheelDiameter
is the diameter of the wheel in meters, and gearRatio
is the gear ratio between the output shaft and the wheel.
This equation also works with converting velocity from m/s to rps.
Position Control#
A Position closed loop can be used to target a specified motor position (in rotations).
Position closed loop is currently supported for all base control output types. The units of the output is determined by the control output type.
In a Position closed loop, the gains should be configured as follows:
\(K_s\) - unused, as there is no target velocity
\(K_v\) - unused, as there is no target velocity
\(K_p\) - output per unit of error in position (output/rotation)
\(K_i\) - output per unit of integrated error in position (output/(rotation*s))
\(K_d\) - output per unit of error derivative in position (output/rps)
// in init function, set slot 0 gains
var slot0Configs = new Slot0Configs();
slot0Configs.kP = 24; // An error of 0.5 rotations results in 12 V output
slot0Configs.kI = 0; // no output for integrated error
slot0Configs.kD = 0.1; // A velocity of 1 rps results in 0.1 V output
m_talonFX.getConfigurator().apply(slot0Configs);
// in init function, set slot 0 gains
configs::Slot0Configs slot0Configs{};
slot0Configs.kP = 24; // An error of 0.5 rotations results in 12 V output
slot0Configs.kI = 0; // no output for integrated error
slot0Configs.kD = 0.1; // A velocity of 1 rps results in 0.1 V output
m_talonFX.GetConfigurator().Apply(slot0Configs);
Once the gains are configured, the Position closed loop control request can be sent to the TalonFX. The control request object has an optional feedforward term that can be used to add an arbitrary value to the output, which can be useful to account for the effects of gravity or friction.
// create a position closed-loop request, voltage output, slot 0 configs
var request = new PositionVoltage(0).withSlot(0);
// set position to 10 rotations
m_talonFX.setControl(request.withPosition(10));
// create a position closed-loop request, voltage output, slot 0 configs
auto request = controls::PositionVoltage{0_tr}.WithSlot(0);
// set position to 10 rotations
m_talonFX.SetControl(request.WithPosition(10_tr));
Motion Magic®#
Motion Magic® is a control mode that provides the benefit of Motion Profiling without needing to generate motion profile trajectory points. When using Motion Magic®, the motor will move to a target position using a motion profile, while honoring the user specified acceleration, maximum velocity (cruise velocity), and optional jerk.
The benefits of this control mode over “simple” PID position closed-looping are:
Control of the mechanism throughout the entire motion (as opposed to racing to the end target position)
Control of the mechanism’s inertia to ensure smooth transitions between set points
Improved repeatability despite changes in battery load
Improved repeatability despite changes in motor load
After gain/settings are determined, the robot controller only needs to periodically set the target position.
There is no general requirement to “wait for the profile to finish”. However, the robot application can poll the sensor position and determine when the motion is finished if need be.
Motion Magic® functions by generating a trapezoidal/S-Curve velocity profile that does not exceed the specified cruise velocity, acceleration, or jerk. This is done automatically by the motor controller.
Note
If the remaining sensor distance to travel is small, the velocity may not reach cruise velocity as this would overshoot the target position. This is often referred to as a “triangle profile”.

If the Motion Magic® jerk is set to a nonzero value, the generated velocity profile is no longer trapezoidal, but instead is a continuous S-Curve (corner points are smoothed).
An S-Curve profile has the following advantaged over a trapezoidal profile:
Reducing oscillation of the mechanism.
Maneuver is more deliberate and reproducible.
Note
The jerk control feature, by its nature, will increase the amount of time a movement requires. This can be compensated for by increasing the configured acceleration value.

The following parameters must be set when controlling using Motion Magic®
Cruise Velocity - peak/cruising velocity of the motion
Acceleration - controls acceleration and deceleration rates during the beginning and end of motion
Jerk - controls jerk, which is the derivative of acceleration
Motion Magic® is currently supported for all base control output types. The units of the output is determined by the control output type.
The Motion Magic® jerk, acceleration, and cruise velocity can be configured in code using a MotionMagicConfigs
(Java, C++) object.
In Motion Magic®, the gains should be configured as follows:
\(K_s\) - output to overcome static friction (output)
\(K_v\) - output per unit of target velocity (output/rps)
\(K_p\) - output per unit of error in position (output/rotation)
\(K_i\) - output per unit of integrated error in position (output/(rotation*s))
\(K_d\) - output per unit of error in velocity (output/rps)
// in init function
var talonFXConfigs = new TalonFXConfiguration();
// set slot 0 gains
var slot0Configs = talonFXConfigs.Slot0Configs;
slot0Configs.kS = 0.25; // Add 0.25 V output to overcome static friction
slot0Configs.kV = 0.12; // A velocity target of 1 rps results in 0.12 V output
slot0Configs.kP = 4.8; // A position error of 2.5 rotations results in 12 V output
slot0Configs.kI = 0; // no output for integrated error
slot0Configs.kD = 0.1; // A velocity error of 1 rps results in 0.1 V output
// set Motion Magic settings
var motionMagicConfigs = talonFXConfigs.MotionMagicConfigs;
motionMagicConfigs.MotionMagicCruiseVelocity = 80; // Target cruise velocity of 80 rps
motionMagicConfigs.MotionMagicAcceleration = 160; // Target acceleration of 160 rps/s (0.5 seconds)
motionMagicConfigs.MotionMagicJerk = 1600; // Target jerk of 1600 rps/s/s (0.1 seconds)
m_talonFX.getConfigurator().apply(talonFXConfigs);
// in init function
configs::TalonFXConfiguration talonFXConfigs{};
// set slot 0 gains
auto& slot0Configs = talonFXConfigs.Slot0Configs;
slot0Configs.kS = 0.25; // Add 0.25 V output to overcome static friction
slot0Configs.kV = 0.12; // A velocity target of 1 rps results in 0.12 V output
slot0Configs.kP = 4.8; // A position error of 2.5 rotations results in 12 V output
slot0Configs.kI = 0; // no output for integrated error
slot0Configs.kD = 0.1; // A velocity error of 1 rps results in 0.1 V output
// set Motion Magic settings
auto& motionMagicConfigs = talonFXConfigs.MotionMagicConfigs;
motionMagicConfigs.MotionMagicCruiseVelocity = 80; // Target cruise velocity of 80 rps
motionMagicConfigs.MotionMagicAcceleration = 160; // Target acceleration of 160 rps/s (0.5 seconds)
motionMagicConfigs.MotionMagicJerk = 1600; // Target jerk of 1600 rps/s/s (0.1 seconds)
m_talonFX.GetConfigurator().Apply(talonFXConfigs);
Tip
Motion Magic® supports modifying jerk and acceleration on the fly (requires firmware version 23.6.10.1 or newer).
Once the gains are configured, the Motion Magic® request can be sent to the TalonFX. The control request object has an optional feedforward term that can be used to add an arbitrary value to the output, which can be useful to account for the effects of gravity.
// create a Motion Magic request, voltage output, slot 0 configs
var request = new MotionMagicVoltage(0).withSlot(0);
// set position to 10 rotations
m_talonFX.setControl(request.withPosition(10));
// create a Motion Magic request, voltage output, slot 0 configs
auto request = controls::MotionMagicVoltage{0_tr}.WithSlot(0);
// set position to 10 rotations
m_talonFX.SetControl(request.WithPosition(10_tr));
TalonFX Remote Sensors#
The TalonFX supports various remote sensors. Remote sensors allow onboard closed-loop functionality at rates faster than a traditional robot processor (~1Khz) by reading the remote sensor directly from the CAN bus. This allows supported motor controllers to execute closed-loop modes with sensor values sourced by supported sensors.
A list of supported remote sensors can be found in the API docs (Java, C++).
Remote sensors can be configured using Tuner X or via code. This document highlights how to configure a remote sensor in a robot program.
RemoteCANcoder
#
A supported motor controller will update its position and velocity whenever the CANcoder publishes its information on the CAN bus.
var fx_cfg = new TalonFXConfiguration();
fx_cfg.Feedback.FeedbackRemoteSensorID = m_cancoder.getDeviceID();
fx_cfg.Feedback.FeedbackSensorSource = FeedbackSensorSourceValue.FusedCANcoder;
m_talonFX.getConfigurator().apply(fx_cfg);
configs::TalonFXConfiguration fx_cfg{};
fx_cfg.Feedback.FeedbackRemoteSensorID = m_cancoder.GetDeviceID();
fx_cfg.Feedback.FeedbackSensorSource = signals::FeedbackSensorSourceValue::FusedCANcoder;
m_talonFX.GetConfigurator().Apply(fx_cfg);
FusedCANcoder
#
New in Phoenix Pro is a feedback sensor type called FusedCANcoder
. FusedCANcoder
will fuse another CANcoder’s information with the motor’s internal rotor, which provides the best possible position and velocity for accuracy and bandwidth. This is useful in applications such as swerve azimuth.
FusedCANcoder
requires the configuration of several Feedback
config group items, shown below.
51 /* Configure CANcoder to zero the magnet appropriately */
52 CANcoderConfiguration cc_cfg = new CANcoderConfiguration();
53 cc_cfg.MagnetSensor.AbsoluteSensorRange = AbsoluteSensorRangeValue.Signed_PlusMinusHalf;
54 cc_cfg.MagnetSensor.SensorDirection = SensorDirectionValue.CounterClockwise_Positive;
55 cc_cfg.MagnetSensor.MagnetOffset = 0.4;
56 m_cc.getConfigurator().apply(cc_cfg);
57
58 TalonFXConfiguration fx_cfg = new TalonFXConfiguration();
59 fx_cfg.Feedback.FeedbackRemoteSensorID = m_cc.getDeviceID();
60 fx_cfg.Feedback.FeedbackSensorSource = FeedbackSensorSourceValue.FusedCANcoder;
61 fx_cfg.Feedback.SensorToMechanismRatio = 1.0;
62 fx_cfg.Feedback.RotorToSensorRatio = 12.8;
63
64 m_fx.getConfigurator().apply(fx_cfg);
11 /* Configure CANcoder to zero the magnet appropriately */
12 configs::CANcoderConfiguration cc_cfg{};
13 cc_cfg.MagnetSensor.AbsoluteSensorRange = signals::AbsoluteSensorRangeValue::Signed_PlusMinusHalf;
14 cc_cfg.MagnetSensor.SensorDirection = signals::SensorDirectionValue::CounterClockwise_Positive;
15 cc_cfg.MagnetSensor.MagnetOffset = 0.4;
16 m_cc.GetConfigurator().Apply(cc_cfg);
17
18 configs::TalonFXConfiguration fx_cfg{};
19 fx_cfg.Feedback.FeedbackRemoteSensorID = m_cc.GetDeviceID();
20 fx_cfg.Feedback.FeedbackSensorSource = signals::FeedbackSensorSourceValue::FusedCANcoder;
21 fx_cfg.Feedback.SensorToMechanismRatio = 1.0;
22 fx_cfg.Feedback.RotorToSensorRatio = 12.8;
23
24 m_fx.GetConfigurator().Apply(fx_cfg);
Usage is the same as any status signal:
fx_pos.refresh();
cc_pos.refresh();
System.out.println("FX Position: " + fx_pos.toString());
System.out.println("CANcoder Position: " + cc_pos.toString());
fx_pos.Refresh();
cc_pos.Refresh();
std::cout << "FX Position: " << fx_pos << std::endl;
std::cout << "CANcoder Position: " << cc_pos << std::endl;
Migration Guide#
This section serves as a “cheat sheet” of commonly-used functions in Phoenix 5 and their equivalents in Phoenix Pro.
- Configuration
Configuring device configs in robot code
- Status Signals
Using status signals to retrieve sensor data from devices
- Control Requests
Using control requests to control the functionality of actuators, such as the TalonFX
- Closed-Loop Control
Configuring and using closed-loop control requests
- Feature Replacements
Other features replaced or improved upon in Phoenix Pro
Configuration#
Phoenix Pro simplifies the configuration process through the use of device-specific Configuration
classes, as well as configuration groups.
Applying Configs#
v5 |
// set slot 0 gains
// 50 ms timeout on each config call
m_motor.config_kF(0, 0.05, 50);
m_motor.config_kP(0, 0.046, 50);
m_motor.config_kI(0, 0.0002, 50);
m_motor.config_kD(0, 0.42, 50);
// set slot 0 gains
// 50 ms timeout on each config call
m_motor.Config_kF(0, 0.05, 50);
m_motor.Config_kP(0, 0.046, 50);
m_motor.Config_kI(0, 0.0002, 50);
m_motor.Config_kD(0, 0.42, 50);
|
Pro |
// set slot 0 gains
var slot0Configs = new Slot0Configs();
slot0Configs.kV = 0.12;
slot0Configs.kP = 0.11;
slot0Configs.kI = 0.5;
slot0Configs.kD = 0.001;
// apply gains, 50 ms total timeout
m_talonFX.getConfigurator().apply(slot0Configs, 0.050);
// set slot 0 gains
configs::Slot0Configs slot0Configs{};
slot0Configs.kV = 0.12;
slot0Configs.kP = 0.11;
slot0Configs.kI = 0.5;
slot0Configs.kD = 0.001;
// apply gains, 50 ms total timeout
m_talonFX.GetConfigurator().Apply(slot0Configs, 50_ms);
|
Factory Defaulting Configs#
v5 |
// user must remember to factory default if they configure devices in code
m_motor.configFactoryDefault();
// user must remember to factory default if they configure devices in code
m_motor.ConfigFactoryDefault();
|
Pro |
// any unmodified configs in a configuration object are *automatically* factory-defaulted;
// user can perform a full factory default by passing a new device configuration object
m_motor.getConfigurator().apply(new TalonFXConfiguration());
// any unmodified configs in a configuration object are *automatically* factory-defaulted;
// user can perform a full factory default by passing a new device configuration object
m_motor.GetConfigurator().Apply(TalonFXConfiguration{});
|
Retrieving Configs#
v5 |
// a limited number of configs have configGet* methods;
// for example, you can get the supply current limits
var supplyCurLim = new SupplyCurrentLimitConfiguration();
m_motor.configGetSupplyCurrentLimit(supplyCurLim);
// a limited number of configs have ConfigGet* methods;
// for example, you can get the supply current limits
SupplyCurrentLimitConfiguration supplyCurLim{};
m_motor.ConfigGetSupplyCurrentLimit(supplyCurLim);
|
Pro |
var fx_cfg = new TalonFXConfiguration();
// fetch *all* configs currently applied to the device
m_motor.getConfigurator().refresh(fx_cfg);
configs::TalonFXConfiguration fx_cfg{};
// fetch *all* configs currently applied to the device
m_motor.GetConfigurator().Refresh(fx_cfg);
|
Status Signals#
Phoenix Pro expands the functionality of status signals with the introduction of the StatusSignalValue
(Java, C++).
Using Status Signals#
v5 |
// get latest TalonFX selected sensor position
// units are encoder ticks
int sensorPos = m_talonFX.getSelectedSensorPosition();
// latency is unknown
// cannot synchronously wait for new data
// get latest TalonFX selected sensor position
// units are encoder ticks
int sensorPos = m_talonFX.GetSelectedSensorPosition();
// latency is unknown
// cannot synchronously wait for new data
|
Pro |
// acquire a refreshed TalonFX rotor position signal
var rotorPosSignal = m_talonFX.getRotorPosition();
// because we are calling getRotorPosition() every loop,
// we do not need to call refresh()
//rotorPosSignal.refresh();
// retrieve position value that we just refreshed
// units are rotations
var rotorPos = rotorPosSignal.getValue();
// get latency of the signal
var rotorPosLatency = rotorPosSignal.getTimestamp().getLatency();
// synchronously wait 20 ms for new data
rotorPosSignal.waitForUpdate(0.020);
// acquire a refreshed TalonFX rotor position signal
auto& rotorPosSignal = m_talonFX.GetRotorPosition();
// because we are calling GetRotorPosition() every loop,
// we do not need to call Refresh()
//rotorPosSignal.Refresh();
// retrieve position value that we just refreshed
// units are rotations, uses the units library
auto rotorPos = rotorPosSignal.GetValue();
// get latency of the signal
auto rotorPosLatency = rotorPosSignal.GetTimestamp().GetLatency();
// synchronously wait 20 ms for new data
rotorPosSignal.WaitForUpdate(20_ms);
|
Changing Update Frequency (Status Frame Period)#
v5 |
// slow down the Status 2 frame (selected sensor data) to 5 Hz (200ms)
m_talonFX.setStatusFramePeriod(StatusFrameEnhanced.Status_2_Feedback0, 200);
// slow down the Status 2 frame (selected sensor data) to 5 Hz (200ms)
m_talonFX.SetStatusFramePeriod(StatusFrameEnhanced::Status_2_Feedback0, 200);
|
Pro |
// slow down the position signal to 5 Hz
m_talonFX.getPosition().setUpdateFrequency(5);
// slow down the position signal to 5 Hz
m_talonFX.GetPosition().SetUpdateFrequency(5_Hz);
|
Important
Currently in Phoenix Pro, when different status signal frequencies are specified for signals that share a status frame, the last specified frequency is applied to the status frame. As a result, users should apply the slowest status frame frequencies first and the fastest frequencies last.
Common Signals#
Several status signals have changed name or form in Phoenix Pro.
General Signals#
Phoenix 5 |
Phoenix Pro |
---|---|
|
|
|
|
|
|
Talon FX Signals#
Phoenix 5 |
Phoenix Pro |
---|---|
|
|
|
StatorCurrent (motoring +, braking -),TorqueCurrent (forward +, reverse -) |
|
|
|
|
|
|
|
|
|
|
CANcoder Signals#
Phoenix 5 |
Phoenix Pro |
---|---|
|
|
Pigeon 2 Signals#
Note
Many Pigeon 2 signal getters in Phoenix 5 fill an array, such as YawPitchRoll
. In Phoenix Pro, these signals have been broken up into their individual components, such as Yaw
, Pitch
, and Roll
.
Phoenix 5 |
Phoenix Pro |
---|---|
|
|
|
|
|
|
|
|
|
|
Control Requests#
Phoenix Pro provides an extensive list of flexible control modes through the use of strongly-typed control requests.
Using Control Requests#
v5 |
// robot init, set voltage compensation to 12 V
m_motor.configVoltageComSaturation(12);
m_motor.enableVoltageCompensation(true);
// main robot code, command 12 V output
m_motor.set(ControlMode.PercentOutput, 1.0);
// robot init, set voltage compensation to 12 V
m_motor.ConfigVoltageComSaturation(12);
m_motor.EnableVoltageCompensation(true);
// main robot code, command 12 V output
m_motor.Set(ControlMode::PercentOutput, 1.0);
|
Pro |
// class member variable
VoltageOut m_request = new VoltageOut(0);
// main robot code, command 12 V output
m_motor.setControl(m_request.withOutput(12.0));
// class member variable
controls::VoltageOut m_request{0_V};
// main robot code, command 12 V output
m_motor.SetControl(m_request.WithOutput(12_V));
|
Follower Motors#
v5 |
// robot init, set m_follower to follow m_leader
m_follower.follow(m_leader);
// m_follower should NOT oppose m_leader
m_follower.setInverted(TalonFXInvertType.FollowMaster);
// set m_strictFollower to follow m_leader
m_strictFollower.follow(m_leader);
// set m_strictFollower to ignore m_leader invert and use its own
m_strictFollower.setInverted(TalonFXInvertType.CounterClockwise);
// main robot code, command 100% output for m_leader
m_leader.set(ControlMode.PercentOutput, 1.0);
// - m_follower and m_strictFollower will also run at 100% output
// - m_follower will follow m_leader's invert, while m_strictFollower
// ignores it and uses its own
// NOTE: if set(), neutralOutput(), or disable() is ever called on
// the followers, they will stop following
// robot init, set m_follower to follow m_leader
m_follower.Follow(m_leader);
// m_follower should NOT oppose m_leader
m_follower.SetInverted(TalonFXInvertType::FollowMaster);
// set m_strictFollower to follow m_leader
m_strictFollower.Follow(m_leader);
// set m_strictFollower to ignore m_leader invert and use its own
m_strictFollower.SetInverted(TalonFXInvertType::CounterClockwise);
// main robot code, command 100% output for m_leader
m_leader.Set(ControlMode::PercentOutput, 1.0);
// - m_follower and m_strictFollower will also run at 100% output
// - m_follower will follow m_leader's invert, while m_strictFollower
// ignores it and uses its own
// NOTE: if Set(), NeutralOutput(), or Disable() is ever called on
// the followers, they will stop following
|
Pro |
// class member variables
DutyCycle m_request = new DutyCycle(0);
// robot init, set m_follower to follow m_leader
// m_follower should NOT oppose leader
m_follower.setControl(new Follower(m_leader.getDeviceID(), false));
// set m_strictFollower to strict-follow m_leader
// strict followers ignore the leader's invert and use their own
m_strictFollower.setControl(new StrictFollower(m_leader.getDeviceID()));
// main robot code, command 100% output for m_leader
m_motor.setControl(m_request.withOutput(1.0));
// - m_follower and m_strictFollower will also run at 100% output
// - m_follower will follow m_leader's invert, while m_strictFollower
// ignores it and uses its own
// class member variables
controls::DutyCycle m_request{0};
// robot init, set m_follower to follow m_leader
// m_follower should NOT oppose leader
m_follower.SetControl(controls::Follower{m_leader.GetDeviceID(), false});
// set m_strictFollower to strict-follow m_leader
// strict followers ignore the leader's invert and use their own
m_strictFollower.SetControl(controls::StrictFollower{m_leader.GetDeviceID()});
// main robot code, command 100% output for m_leader
m_motor.SetControl(m_request.WithOutput(1.0));
// - m_follower and m_strictFollower will also run at 100% output
// - m_follower will follow m_leader's invert, while m_strictFollower
// ignores it and uses its own
|
Control Types#
In Phoenix Pro, voltage compensation has been replaced with the ability to directly specify the control output type.
All control output types are supported in open-loop and closed-loop control requests.
Phoenix 5 |
Phoenix Pro |
---|---|
PercentOutput |
DutyCycleOut |
PercentOutput + Voltage Compensation |
VoltageOut |
Phoenix 5 does not support torque control |
TorqueCurrentFOC |
Current closed-loop |
This has been deprecated in Phoenix Pro.
|
Phoenix 5 |
Phoenix Pro |
---|---|
Position |
PositionDutyCycle |
Velocity |
VelocityDutyCycle |
MotionMagic |
MotionMagicDutyCycle |
Closed-loop + Voltage Compensation |
{ClosedLoop}Voltage |
Closed-loop + Torque Control (not supported in Phoenix 5) |
{ClosedLoop}TorqueCurrentFOC |
Closed-Loop Control#
Phoenix Pro enhances the experience of using onboard closed-loop control through the use of standardized units and a variety of control output types.
Closed Loop Gains#
These tables are for translating Phoenix 5 gains to Phoenix Pro DutyCycle gains.
Note
There are other control output types in Phoenix Pro that will change the magnitude of the gains.
Position (DutyCycle)#

Velocity (DutyCycle)#

Using Closed-Loop Control#
v5 |
// robot init, set slot 0 gains
m_motor.config_kF(0, 0.05, 50);
m_motor.config_kP(0, 0.046, 50);
m_motor.config_kI(0, 0.0002, 50);
m_motor.config_kD(0, 4.2, 50);
// enable voltage compensation
m_motor.configVoltageComSaturation(12);
m_motor.enableVoltageCompensation(true);
// periodic, run velocity control with slot 0 configs,
// target velocity of 50 rps (10240 ticks/100ms)
m_motor.selectProfileSlot(0, 0);
m_motor.set(ControlMode.Velocity, 10240);
// robot init, set slot 0 gains
m_motor.Config_kF(0, 0.05, 50);
m_motor.Config_kP(0, 0.046, 50);
m_motor.Config_kI(0, 0.0002, 50);
m_motor.Config_kD(0, 4.2, 50);
// enable voltage compensation
m_motor.ConfigVoltageComSaturation(12);
m_motor.EnableVoltageCompensation(true);
// periodic, run velocity control with slot 0 configs,
// target velocity of 50 rps (10240 ticks/100ms)
m_motor.SelectProfileSlot(0, 0);
m_motor.Set(ControlMode::Velocity, 10240);
|
Pro |
// class member variable
VelocityVoltage m_velocity = new VelocityVoltage(0);
// robot init, set slot 0 gains
var slot0Configs = new Slot0Configs();
slot0Configs.kV = 0.12;
slot0Configs.kP = 0.11;
slot0Configs.kI = 0.5;
slot0Configs.kD = 0.01;
m_talonFX.getConfigurator().apply(slot0Configs, 0.050);
// periodic, run velocity control with slot 0 configs,
// target velocity of 50 rps
m_velocity.Slot = 0;
m_motor.setControl(m_velocity.withVelocity(50));
// class member variable
controls::VelocityVoltage m_velocity{0_tps};
// robot init, set slot 0 gains
configs::Slot0Configs slot0Configs{};
slot0Configs.kV = 0.12;
slot0Configs.kP = 0.11;
slot0Configs.kI = 0.5;
slot0Configs.kD = 0.01;
m_talonFX.GetConfigurator().Apply(slot0Configs, 50_ms);
// periodic, run velocity control with slot 0 configs,
// target velocity of 50 rps
m_velocity.Slot = 0;
m_motor.SetControl(m_velocity.WithVelocity(50_tps));
|
Motion Magic®#
v5 |
// robot init, set slot 0 gains
m_motor.config_kF(0, 0.05, 50);
// PID runs on position
m_motor.config_kP(0, 0.2, 50);
m_motor.config_kI(0, 0, 50);
m_motor.config_kD(0, 4.2, 50);
// set Motion Magic settings
m_motor.configMotionCruiseVelocity(16384); // 80 rps = 16384 ticks/100ms cruise velocity
m_motor.configMotionAcceleration(32768); // 160 rps/s = 32768 ticks/100ms/s acceleration
m_motor.configMotionSCurveStrength(3); // s-curve smoothing strength of 3
// enable voltage compensation
m_motor.configVoltageComSaturation(12);
m_motor.enableVoltageCompensation(true);
// periodic, run Motion Magic with slot 0 configs
m_motor.selectProfileSlot(0, 0);
// target position of 200 rotations (409600 ticks)
// add 0.02 (2%) arbitrary feedforward to overcome friction
m_motor.set(ControlMode.MotionMagic, 409600, DemandType.ArbitraryFeedforward, 0.02);
// robot init, set slot 0 gains
m_motor.Config_kF(0, 0.05, 50);
// PID runs on position
m_motor.Config_kP(0, 0.2, 50);
m_motor.Config_kI(0, 0, 50);
m_motor.Config_kD(0, 4.2, 50);
// set Motion Magic settings
m_motor.ConfigMotionCruiseVelocity(16384); // 80 rps = 16384 ticks/100ms cruise velocity
m_motor.ConfigMotionAcceleration(32768); // 160 rps/s = 32768 ticks/100ms/s acceleration
m_motor.ConfigMotionSCurveStrength(3); // s-curve smoothing strength of 3
// enable voltage compensation
m_motor.ConfigVoltageComSaturation(12);
m_motor.EnableVoltageCompensation(true);
// periodic, run Motion Magic with slot 0 configs
m_motor.SelectProfileSlot(0, 0);
// target position of 200 rotations (409600 ticks)
// add 0.02 (2%) arbitrary feedforward to overcome friction
m_motor.Set(ControlMode::MotionMagic, 409600, DemandType::ArbitraryFeedforward, 0.02);
|
Pro |
Note The Motion Magic® S-Curve Strength has been replaced with jerk control in Phoenix Pro. // class member variable
MotionMagicVoltage m_motmag = new MotionMagicVoltage(0);
// robot init
var talonFXConfigs = new TalonFXConfiguration();
// set slot 0 gains
var slot0Configs = talonFXConfigs.Slot0Configs;
slot0Configs.kS = 0.24; // add 0.24 V to overcome friction
slot0Configs.kV = 0.12; // apply 12 V for a target velocity of 100 rps
// PID runs on position
slot0Configs.kP = 4.8;
slot0Configs.kI = 0;
slot0Configs.kD = 0.1;
// set Motion Magic settings
var motionMagicConfigs = talonFXConfigs.MotionMagicConfigs;
motionMagicConfigs.MotionMagicCruiseVelocity = 80; // 80 rps cruise velocity
motionMagicConfigs.MotionMagicAcceleration = 160; // 160 rps/s acceleration (0.5 seconds)
motionMagicConfigs.MotionMagicJerk = 1600; // 1600 rps/s^2 jerk (0.1 seconds)
m_talonFX.getConfigurator().apply(talonFXConfigs, 0.050);
// periodic, run Motion Magic with slot 0 configs,
// target position of 200 rotations
m_motmag.Slot = 0;
m_motor.setControl(m_motmag.withPosition(200));
// class member variable
controls::MotionMagicVoltage m_motmag{0_tr};
// robot init
configs::TalonFXConfiguration talonFXConfigs{};
// set slot 0 gains
auto& slot0Configs = talonFXConfigs.Slot0Configs;
slot0Configs.kS = 0.24; // add 0.24 V to overcome friction
slot0Configs.kV = 0.12; // apply 12 V for a target velocity of 100 rps
// PID runs on position
slot0Configs.kP = 4.8;
slot0Configs.kI = 0;
slot0Configs.kD = 0.1;
// set Motion Magic settings
auto& motionMagicConfigs = talonFXConfigs.MotionMagicConfigs;
motionMagicConfigs.MotionMagicCruiseVelocity = 80; // 80 rps cruise velocity
motionMagicConfigs.MotionMagicAcceleration = 160; // 160 rps/s acceleration (0.5 seconds)
motionMagicConfigs.MotionMagicJerk = 1600; // 1600 rps/s^2 jerk (0.1 seconds)
m_talonFX.GetConfigurator().Apply(talonFXConfigs, 50_ms);
// periodic, run Motion Magic with slot 0 configs,
// target position of 200 rotations
m_motmag.Slot = 0;
m_motor.SetControl(m_motmag.WithPosition(200_tr));
|
Motion Profiling#
The Motion Profile Executor is not supported in the current release of Phoenix Pro. Users can use Motion Magic® or run a motion profile on the robot controller.
Feature Replacements#
In addition to the changes shown in the other sections, several other Phoenix 5 features have been replaced or improved upon in Phoenix Pro.
Motor Invert#
In Phoenix Pro, motor invert is now a persistent config (Java, C++) instead of a control signal.
Neutral Mode#
In Phoenix Pro, Neutral mode is now available in API as a config (Java, C++). Many control requests also have the ability to override the neutral mode to either force braking (Java, C++) or force coasting (Java, C++).
Nominal Output#
The Talon FX forward and reverse Nominal Output configs have been removed in Phoenix Pro.
The typical use case of the nominal output configs is to overcome friction in closed-loop control modes, which can now be achieved using the kS
feedforward parameter (Java, C++).
Sensor Phase#
The Talon FX setSensorPhase()
method has been removed in Phoenix Pro.
The Talon FX integrated sensor is always in phase, so the method does nothing in Phoenix 5.
When using a remote sensor, you can invert the remote sensor to bring it in phase with the Talon FX.
Sensor Initialization Strategy#
The Talon FX and CANcoder sensors are always initialized to their absolute position in Phoenix Pro.
Velocity Measurement Period/Window#
In Phoenix Pro, the velocity rolling average window in Talon FX and CANcoder has been replaced with a Kalman filter, resulting in a less noisy velocity signal with a minimal impact on latency. As a result, the velocity measurement period/window configs are no longer necessary in Phoenix Pro and have been removed.
Integral Zone#
Phoenix Pro automatically prevents integral windup in closed-loop controls. As a result, the Integral Zone config is no longer necessary and has been removed.
Simulation#
Phoenix Pro supports comprehensive simulation support. All hardware features are available in simulation, including configs, control requests, simulated CAN bus timing, and Phoenix Tuner X support.
Introduction to Simulation#
Supported Devices#
Currently, all Phoenix Pro devices are supported in simulation.
Warning
Multiple CAN buses using the CANivore API is not supported at this time. All CAN devices will appear on the same CAN bus. If you wish to run your robot code in simulation, ensure devices have unique IDs across CAN buses.
Simulation API#
Each supported device has a device-specific SimState
object that can be used to manage I/O with the simulated device. The object can be retrieved by calling getSimState()
on an instance of a device.
var talonFXSim = m_talonFX.getSimState();
auto& talonFXSim = m_talonFX.GetSimState();
Note
Phoenix Pro utilizes the C++ units library when applicable.
Orientation#
The SimState
API ignores typical device invert settings, as the user may change invert for any reason (such as flipping which direction is forward for a drivebase). As a result, for some devices, the SimState
object supports specifying the orientation of the device relative to the robot chassis (Java, C++).
This orientation represents the mechanical linkage between the device and the robot chassis. It should not be changed with runtime invert, as runtime invert specifies the logical orientation of the device. Rather, the orientation should only be modified when the mechanical linkage itself changes, such as when switching between two gearboxes inverted from each other.
var leftTalonFXSim = m_leftTalonFX.getSimState();
var rightTalonFXSim = m_rightTalonFX.getSimState();
// left drivetrain motors are typically CCW+
leftTalonFXSim.Orientation = ChassisReference.CounterClockwise_Positive;
// right drivetrain motors are typically CW+
rightTalonFXSim.Orientation = ChassisReference.Clockwise_Positive;
auto& leftTalonFXSim = m_leftTalonFX.GetSimState();
auto& rightTalonFXSim = m_rightTalonFX.GetSimState();
// left drivetrain motors are typically CCW+
leftTalonFXSim.Orientation = sim::ChassisReference::CounterClockwise_Positive;
// right drivetrain motors are typically CW+
rightTalonFXSim.Orientation = sim::ChassisReference::Clockwise_Positive;
Inputs and Outputs#
All SimState
objects contain multiple inputs to manipulate the state of the device based on simulation physics calculations. For example, all device SimState
objects have a supply voltage input:
Important
Non-FRC platforms are required to set supply voltage, as it affects simulation calculations. It’s recommended that FRC users set supply voltage to getBatteryVoltage()
(Java, C++) to take advantage of WPILib’s BatterySim
(Java, C++) API.
// set the supply voltage of the TalonFX to 12 V
m_talonFXSim.setSupplyVoltage(12);
// set the supply voltage of the TalonFX to 12 V
m_talonFXSim.SetSupplyVoltage(12_V);
Some device SimState
objects also contain outputs that can be used in simulation physics calculations. For example, the TalonFXSimState
(Java, C++) object has a motor voltage output that can be used to calculate position and velocity:
// get the motor voltage of the TalonFX
var motorVoltage = m_talonFXSim.getMotorVoltage();
// use the motor voltage to calculate new position and velocity using an external MotorSimModel class
m_motorSimModel.setMotorVoltage(motorVoltage);
m_motorSimModel.update(0.020); // assume 20 ms loop time
// apply the new rotor position and velocity to the TalonFX
m_talonFXSim.setRawRotorPosition(m_motorSimModel.getPosition());
m_talonFXSim.setRotorVelocity(m_motorSimModel.getVelocity());
// get the motor voltage of the TalonFX
auto motorVoltage = m_talonFXSim.GetMotorVoltage();
// use the motor voltage to calculate new position and velocity using an external MotorSimModel class
m_motorSimModel.SetMotorVoltage(motorVoltage);
m_motorSimModel.Update(20_ms); // assume 20 ms loop time
// apply the new rotor position and velocity to the TalonFX
m_talonFXSim.SetRawRotorPosition(m_motorSimModel.GetPosition());
m_talonFXSim.SetRotorVelocity(m_motorSimModel.GetVelocity());
High Fidelity CAN Bus Simulation#
Many popular CTR Electronics CAN devices support high-fidelity simulation, where the influence of the CAN bus is simulated at a level similar to what happens on a real robot. This means that the timing behavior of control and status signals in simulation will align to the same framing intervals seen on a real CAN bus. In simulation, this may appear as a delay between setting a signal and getting its real value, or between setting its real value and getting it in API.
The update rate can be modified for simulation by wrapping the signal’s frequency in a RobotBase.isSimulation()
(Java, C++) condition.
if (RobotBase.isSimulation()) {
m_velocitySignal.setUpdateFrequency(1000); // set update rate to 1ms
}
if (RobotBase::IsSimulation()) {
m_velocitySignal.SetUpdateFrequency(1000_Hz); // set update rate to 1ms
}
WPILib Integration#
Phoenix Pro API used as part of WPILib robot projects provides implementations of common WPILib interfaces that FRC teams use.
MotorController Integration#
Phoenix Pro motor controller classes such as TalonFX
(Java, C++) implement the MotorController
(Java, C++) interface. This allows Phoenix Pro motor controllers to be used in WPILib drivetrain classes such as DifferentialDrive
.
// instantiate motor controllers
TalonFX m_motorLeft = new TalonFX(0);
TalonFX m_motorRight = new TalonFX(1);
// create differentialdrive object for robot control
DifferentialDrive m_diffDrive = new DifferentialDrive(m_motorLeft, m_motorRight);
// instantiate joystick
XboxController m_driverJoy = new XboxController(0);
public void teleopPeriodic() {
var forward = -m_driverJoy.getLeftY();
var rot = -m_driverJoy.getRightX();
m_diffDrive.arcadeDrive(forward, rot);
}
void Robot::TeleopPeriodic() {
auto forward = -m_driverJoy.GetLeftY();
auto rot = -m_driverJoy.GetRightX();
m_diffDrive.ArcadeDrive(forward, rot);
}
// instantiate motor controllers
hardware::TalonFX m_motorLeft{0};
hardware::TalonFX m_motorRight{1};
// create differentialdrive object for robot control
frc::DifferentialDrive m_diffDrive{m_motorLeft, m_motorRight};
// instantiate joystick
frc::XboxController m_driverJoy{0};
Motor Safety#
CTR Electronics supported actuators implement WPILib Motor Safety. In additional to the normal enable signal of CTR Electronics actuators, Motor Safety will automatically disable the device according to the WPILib Motor Safety implementation.
Simulation#
It’s recommended that users set supply voltage to getBatteryVoltage()
(Java, C++) to take advantage of WPILib’s BatterySim
(Java, C++) API. Additionally, the simulated device state is shown in the simulation Other Devices menu.

Gyro Integration#
CTR Electronics IMUs, such as the Pigeon 2.0, implement the WPILib Gyro
(Java, C++) interface.
Note
calibrate()
does nothing on the Pigeon 2.0, as it does not require manual calibration.
Simulation#
The simulated device state is shown in the simulation Other Devices menu.

Examples#
Comprehensive API usage examples and tutorials.
Open-Loop Quickstart#
The below example showcases controlling a four-motor drivetrain.
Declaring Motor Controllers#
The TalonFX
motor controller constructor (Java, C++) requires a device ID (int) and an optional CAN bus (string).
Note
The name of the native roboRIO CAN bus is rio
. This is also the default CAN bus on the roboRIO when none is specified.
public class Robot extends TimedRobot {
private static final String kCANBus = "canivore";
private final TalonFX m_leftLeader = new TalonFX(0, kCANBus);
private final TalonFX m_rightLeader = new TalonFX(1, kCANBus);
private final TalonFX m_leftFollower = new TalonFX(2, kCANBus);
private final TalonFX m_rightFollower = new TalonFX(3, kCANBus);
}
class Robot : public frc::TimedRobot {
private:
static constexpr char const *kCANBus{"canivore"};
ctre::phoenixpro::hardware::TalonFX m_leftLeader{0, kCANBus};
ctre::phoenixpro::hardware::TalonFX m_rightLeader{1, kCANBus};
ctre::phoenixpro::hardware::TalonFX m_leftFollower{2, kCANBus};
ctre::phoenixpro::hardware::TalonFX m_rightFollower{3, kCANBus};
}
Configure Followers & Inverts#
In a traditional robot drivetrain, there are two motors attached to each horizontal side of the drivetrain. This setup typically (unless mechanically inverted) causes the right side to rotate in an opposite direction when given the same voltage.

@Override
public void robotInit() {
// start with factory-default configs
var currentConfigs = new MotorOutputConfigs();
// The left motor is CCW+
currentConfigs.Inverted = InvertedValue.CounterClockwise_Positive;
m_leftLeader.getConfigurator().apply(currentConfigs);
// The right motor is CW+
currentConfigs.Inverted = InvertedValue.Clockwise_Positive;
m_rightLeader.getConfigurator().apply(currentConfigs);
// Ensure our followers are following their respective leader
m_leftFollower.setControl(new Follower(m_leftLeader.getDeviceID(), false));
m_rightFollower.setControl(new Follower(m_rightLeader.getDeviceID(), false));
}
#include "Robot.h"
using namespace ctre::phoenixpro;
void Robot::RobotInit() {
// start with factory-default configs
configs::MotorOutputConfigs currentConfigs{};
// The left motor is CCW+
currentConfigs.Inverted = signals::InvertedValue::CounterClockwise_Positive;
m_leftLeader.GetConfigurator().Apply(currentConfigs);
// The right motor is CW+
currentConfigs.Inverted = signals::InvertedValue::Clockwise_Positive;
m_rightLeader.GetConfigurator().Apply(currentConfigs);
// Ensure the followers are following their respective leader
m_leftFollower.SetControl(controls::Follower{m_leftLeader.GetDeviceID(), false});
m_rightFollower.SetControl(controls::Follower{m_rightLeader.GetDeviceID(), false});
}
Full Example#
public class Robot extends TimedRobot {
private static final String kCANBus = "canivore";
private final TalonFX m_leftLeader = new TalonFX(0, kCANBus);
private final TalonFX m_rightLeader = new TalonFX(1, kCANBus);
private final TalonFX m_leftFollower = new TalonFX(2, kCANBus);
private final TalonFX m_rightFollower = new TalonFX(3, kCANBus);
private final DutyCycleOut m_leftOut = new DutyCycleOut(0);
private final DutyCycleOut m_rightOut = new DutyCycleOut(0);
private final XboxController m_driverJoy = new XboxController(0);
@Override
public void robotInit() {
// start with factory-default configs
var currentConfigs = new MotorOutputConfigs();
// The left motor is CCW+
currentConfigs.Inverted = InvertedValue.CounterClockwise_Positive;
m_leftLeader.getConfigurator().apply(currentConfigs);
// The right motor is CW+
currentConfigs.Inverted = InvertedValue.Clockwise_Positive;
m_rightLeader.getConfigurator().apply(currentConfigs);
// Ensure our followers are following their respective leader
m_leftFollower.setControl(new Follower(m_leftLeader.getDeviceID(), false));
m_rightFollower.setControl(new Follower(m_rightLeader.getDeviceID(), false));
}
@Override
public void teleopPeriodic() {
// retrieve joystick inputs
var fwd = -m_driverJoy.getLeftY();
var rot = m_driverJoy.getRightX();
// modify control requests
m_leftOut.Output = fwd + rot;
m_rightOut.Output = fwd - rot;
// send control requests
m_leftLeader.setControl(m_leftOut);
m_rightLeader.setControl(m_rightOut);
}
}
#include "Robot.h"
using namespace ctre::phoenixpro;
void Robot::RobotInit() {
// start with factory-default configs
configs::MotorOutputConfigs currentConfigs{};
// The left motor is CCW+
currentConfigs.Inverted = signals::InvertedValue::CounterClockwise_Positive;
m_leftLeader.GetConfigurator().Apply(currentConfigs);
// The right motor is CW+
currentConfigs.Inverted = signals::InvertedValue::Clockwise_Positive;
m_rightLeader.GetConfigurator().Apply(currentConfigs);
// Ensure the followers are following their respective leader
m_leftFollower.SetControl(controls::Follower{m_leftLeader.GetDeviceID(), false});
m_rightFollower.SetControl(controls::Follower{m_rightLeader.GetDeviceID(), false});
}
void Robot::TeleopPeriodic() {
// retrieve joystick inputs
auto fwd = -m_driverJoy.GetLeftY();
auto rot = m_driverJoy.GetRightX();
// modify control requests
m_leftOut.Output = fwd + rot;
m_rightOut.Output = fwd - rot;
// send control requests
m_leftLeader.SetControl(m_leftOut);
m_rightLeader.SetControl(m_rightOut);
}
private:
static constexpr char const *kCANBus{"canivore"};
ctre::phoenixpro::hardware::TalonFX m_leftLeader{0, kCANBus};
ctre::phoenixpro::hardware::TalonFX m_rightLeader{1, kCANBus};
ctre::phoenixpro::hardware::TalonFX m_leftFollower{2, kCANBus};
ctre::phoenixpro::hardware::TalonFX m_rightFollower{3, kCANBus};
ctre::phoenixpro::controls::DutyCycleOut m_leftOut{0};
ctre::phoenixpro::controls::DutyCycleOut m_rightOut{0};
frc::XboxController m_driverJoy{0};
CANivore Intro#
The CANivore is a multipurpose USB-to-CAN FD device. The CANivore:
Adds a secondary CAN FD bus to the roboRIO
CAN FD improves upon CAN with increased device bandwidth and transfer speed.
Allows the control of CTR Electronics devices on non-roboRIO platforms.
Important
Details on licensing your CANivore is available on the licensing page.
CANivore Setup#
Supported Systems#
Currently, the following systems are supported for CANivore development:
NI roboRIO
Windows 10/11 x86-64
Linux x86-64 (desktop)
Ubuntu 22.04 or newer
Debian Bullseye or newer
Linux ARM32 and ARM64 (Raspberry Pi, NVIDIA Jetson)
Ubuntu 20.04 or newer
Debian Bullseye or newer
Note
Custom bit rates and CAN 2.0 are not supported at this time. The parameters passed into SocketCAN are not applied by the firmware.
roboRIO#
Note
Phoenix Tuner X requires a 2023 roboRIO image or newer to configure the CANivore.
No additional steps are required. The roboRIO comes with the canivore-usb
kernel module pre-installed.
Linux (non-FRC)#
On non-FRC Linux systems, the canivore-usb
kernel module must be installed to add SocketCAN support for the CANivore. The kernel module is distributed through our APT repository. Begin with adding the repository to your APT sources.
sudo curl -s --compressed -o /usr/share/keyrings/ctr-pubkey.gpg "https://deb.ctr-electronics.com/ctr-pubkey.gpg"
sudo curl -s --compressed -o /etc/apt/sources.list.d/ctr<year>.list "https://deb.ctr-electronics.com/ctr<year>.list"
Note
<year>
should be replaced with the year of Phoenix Pro software for which you have purchased licenses.
After adding the sources, the kernel module can be installed and updated using the following:
sudo apt update
sudo apt install canivore-usb
Tip
To get a robot application up and running quickly, check out our non-FRC Linux example.
Viewing Attached CANivores#
Attached CANivores can be viewed in Phoenix Tuner X by selecting the CANivores page from the left-hand sidebar. You can specify the target system in the Target IP or Team # text box.

Note
The Phoenix Diagnostic Server must be running on the target system to use the CANivores page.
Tip
If you are connecting to CANivores on your local Windows machine, you can enable the CANivore USB toggle and set the target IP to localhost
. This runs a diagnostic server within Tuner X so you do not need to run a robot project to communicate with CANivores.
Field Upgrading CANivores#
A CANivore can be field updated using Phoenix Tuner X.
Click or tap on the listed CANivore card:

The CANivore can then be field upgraded via the dropdown or by manually selected a file:

Phoenix Tuner X also allows the user to batch field upgrade CANivores from the list of CANivores in the same manner as batch field upgrading devices.
Renaming CANivores#
CANivores can be given custom names for use within a robot program. This can be configured through Phoenix Tuner X on the specified device card.

CANivore API#
All device constructors have an overload that takes a string CAN bus identifier. This identifier can be rio
for the native roboRIO CAN bus, *
to select the first available CANivore, or a CANivore’s name or serial number. On non-FRC Linux systems, this string can also be a SocketCAN interface.
Note
If there are multiple CANivores with the same name, the system will use the first CANivore found.
If no CAN bus string is passed into the constructor, or the CAN bus string is empty, the behavior is platform-dependent:
roboRIO: use the roboRIO native CAN bus
Windows: use the first CANivore found
non-FRC Linux: use SocketCAN interface
can0
TalonFX fx_default = new TalonFX(0); // On roboRIO, this constructs a TalonFX on the RIO native CAN bus
TalonFX fx_rio = new TalonFX(1, "rio"); // This also constructs a TalonFX on the RIO native CAN bus
TalonFX fx_drivebase = new TalonFX(0, "Drivebase"); // This constructs a TalonFX on the CANivore bus named "Drivebase"
CANcoder cc_elevator = new CANcoder(0, "Elevator"); // This constructs a CANcoder on the CANivore bus named "Elevator"
hardware::TalonFX fx_default{0}; // On roboRIO, this constructs a TalonFX on the RIO native CAN bus
hardware::TalonFX fx_rio{1, "rio"}; // This also constructs a TalonFX on the RIO native CAN bus
hardware::TalonFX fx_drivebase{0, "Drivebase"}; // This constructs a TalonFX on the CANivore bus named "Drivebase"
hardware::CANcoder cc_elevator{0, "Elevator"}; // This constructs a CANcoder on the CANivore bus named "Elevator"
CANivore Status Prints#
When working with CANivore CAN buses in a robot program, Phoenix prints some messages to report the state of the CANivore connection. These messages can be useful to debug connection issues (bad USB vs bad CAN) or report bugs to CTR Electronics.
Message |
Connection Status |
---|---|
CANbus Failed to Connect |
Could not connect to a CANivore with the given name or serial number |
CANbus Connected |
Successfully found and connected to the CANivore with the given name or serial number |
CANbus Disconnected |
Detected that a CANivore USB device has been disconnected |
Message |
Bring-up Status |
---|---|
CANbus Failed Bring-up |
Found and connected to the CANivore, but could not configure the device or start the network |
CANbus Successfully Started |
Successfully configured the CANivore and started the network |
Message |
Network State |
---|---|
CANbus Network Down |
Linux: The SocketCAN network has been deactivated, USB-to-CAN activity has stopped
Windows: Could not open the communication channels for USB-to-CAN traffic
|
CANbus Network Up |
Linux: The SocketCAN network has been activated, USB-to-CAN activity has resumed
Windows: Successfully opened the communication channels for USB-to-CAN traffic
|
Hardware-Attached Simulation#
CANivore supports hardware-attached simulation when used in an FRC robot program. This allows a CANivore to be used with real devices on supported host operating systems. The below video showcases controlling a real Falcon 500 in a robot program using hardware-attached simulation.

To utilize hardware-attached simulation, ensure the CANivore is connected directly via USB to the machine running the simulation. All devices on the CANivore CAN Bus should be independently powered, as the CANivore does not provide power. In the robot program, the CANivore name or *
must be specified in the device constructor.
Important
Any motors/actuators that have been connected to a roboRIO CAN Bus at any time must be factory defaulted due to them being FRC Locked. Factory defaulting can be done in Tuner X and should be done when the CANivore is not connected to a roboRIO.
TalonFX m_motor = new TalonFX(0, "mycanivore");
hardware::TalonFX m_motor{0, "mycanivore"};
In VS Code, select the 3 dots in the top-right, then select Hardware Sim Robot Code

A message in the console should appear that the CAN Bus is connected.
********** Robot program startup complete **********
[phoenix] CANbus Connected: uno (WinUSB, 2B189E633353385320202034383803FF)
[phoenix] CANbus Network Up: uno (WinUSB, 2B189E633353385320202034383803FF)
[phoenix] Library initialization is complete.
Advanced Configuration#
The CANivore provides additional configuration options for advanced users.
CAN Bus Termination#
The CANivore has a 120 \(\Omega\) programmable resister for terminating the CAN bus. The resistor can be configured using the CAN Bus Termination toggle in the CANivore device card in Phoenix Tuner X.
Warning
A CAN bus requires two termination resistors, one at each extreme end. If only one is present, communication over CAN may fail.

caniv - CANivore CLI#
caniv
is a Command-line Interface (CLI) to interact with CANivores outside of Phoenix Tuner X.
Note
Unlike the CANivores page in Phoenix Tuner X, caniv
does not require a running Phoenix Diagnostic Server.
On Linux systems (including the roboRIO), caniv
can be found at /usr/local/bin
. On Windows systems, the program is in the Phoenix Tuner X application cache directory, which can be opened by opening the Diagnostic Log page and clicking the left folder icon in the top right:

To view a list of available commands, run caniv
either with no parameters or with --help
.

Troubleshooting#
CAN Bus Troubleshooting#
There are typically two failure modes that must be resolved:
There are same-model devices on the bus with the same device ID (devices have a default device ID of ‘0’).
CAN bus is not wired correctly or robustly
During hardware validation, you will likely have to isolate each device to assign a unique device ID.
Note
CTRE software has the ability to resolve device ID conflicts without device isolation, and CAN bus is capable of reporting the health of the CAN bus (see Driver Station lightening tab). However, the problem is when both root-causes are occurring at the same time, this can confuse students who have no experience with CAN bus systems.
Note
Many teams will pre-assign and update devices (Talon SRXs for example) long before the robot takes form. This is also a great task for new students who need to start learning the control system (with the appropriate mentor oversight to ensure hardware does not get damaged).
Identifying Duplicate IDs#
Tip
Label the devices appropriately so there is no guessing which device ID is what. Don’t have a label maker? Use tape and/or Sharpie (sharpie marks can be removed with alcohol).
Phoenix Tuner X will report when there are multiple devices of the same model with the same ID. This is shown when the device card is RED and there is a message in the middle of the device card. Users seeing this should iteratively reassign IDs on the device(s).
Check your wiring#
Specific wiring instructions can be found in the user manual of each product, but there are common steps that must be followed for all devices:
If connectors are used for CAN bus, tug-test each individual crimped wire one at a time. Bad crimps/connection points are the most common cause of intermittent connection issues.
Confirm red and black are not flipped.
Confirm battery voltage is adequate (through Driver Station or through voltmeter).
Manually inspect and confirm that green-connects-to-green and yellow-connects-to-yellow at every connection point. Flipping/mixing green and yellow is a common failure point during hardware bring up.
Confirm breakers are installed in the PDP where appropriate.
Measure resistance between CANH and CANL when system is not powered (should measure ~60Ω). If the measurement is 120Ω, then confirm both RIO and PDP are in circuit, and PDP jumper is in the correct location.
LEDs are red - now what?#
We need to rule out same-ID versus bad-bus-wiring.
There are two approaches:
Approach 1 will help troubleshoot bad wiring and common IDs.
Approach 2 will only be effective in troubleshooting common IDs, but this method is noteworthy because it is simple/quick (no wiring changes, just pull breakers).
The specific instructions for changing device ID are in the next section. Review this if needed.
Approach 1 (best)#
Physically connect CAN bus from roboRIO to one device only. Circumvent your wiring if need be.
Power boot robot/bench setup.
Open Phoenix Tuner X and wait for connection (roboRIO may take ~30 seconds to boot)
Open the Devices page
Confirm that CAN device appears
Use Tuner X to change the device ID
Label the new ID on the physical device
Repeat this procedure for every device, one at a time
If you find a particular device where communication is not possible, scrutinize device’s power and CAN connection to the system. Make the test setup so simple that the only failure mode possible is within the device itself.
Note
Typically, there must be two 120-\(\Omega\) termination resistors at each end of the bus. CTR Electronics integrates termination resistors into the PDP and the CANivore. The roboRIO also has an integrated termination resistor. During bring-up, if you keep your harness short (such as the CAN pigtail leads from a single TalonFX) then a single resistor is adequate for testing purposes.
Approach 2 (easier)#
Leave CAN bus wiring as is
Pull breakers and PCM fuse from PDP
Disconnect CAN bus pigtail from PDP
Pick the first device to power up and restore breaker/fuse/pigtail so that only this CAN device is powered
Power boot robot/bench setup
Open Phoenix Tuner X and wait for connection (roboRIO may take ~30 seconds to boot)
Open the Devices page
Confirm that CAN device appears
If device does not appear, scrutinize device’s power and CAN connection to the system
Use Tuner X to change the device ID
Label the new ID on the physical device
Repeat this procedure for every device
If you find a particular device or section of devices where communication is not possible, then the CAN bus wiring needs to be re-inspected. Remember to “flick” / “shake” / “jostle” the CAN wiring in various sections to attempt to reproduce red LED blips. This is a sure sign of loose contact points.
If you are able to detect and change device ID on your devices individually, begin piecing your CAN bus together. Start with either roboRIO <—-> device <—> PDP, or CANivore <—-> device <—> 120 \(\Omega\) resistor, to ensure termination exists at both ends. Then introduce the remaining devices until a failure is observed or until all devices are in-circuit.
If introducing a new device creates a failure symptom, scrutinize that device by replacing it, inspecting common wires, and inspecting power.
At the end of this section, all devices should appear (notwithstanding the above notes) and device LEDs should not be red. TalonFX and Pigeon2 typically blink orange when they are healthy and not controlled, and CANcoder rapid-blinks brightly. PDP may be orange or green depending on its sticky faults.
Support#
CTR Electronics prides itself on excellent customer service. Our contact information can be found on our website.