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.


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.


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();


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.


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.


The refresh() method can be method-chained. As a result, you can call refresh() and getValue() on one line.


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.


If you want to zero your sensors, you can use this API to ensure the set operation has completed before continuing program flow.


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
// wait up to 1 robot loop iteration (20ms) for fresh data

Changing Update Frequency#

All signals can have their update frequency configured via the setUpdateFrequency() method.


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
// slow down supply voltage reporting to 10 Hz


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++).


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.


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++).


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());


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.


getDataCopy() returns a new SignalMeasurement object every call. Java users should avoid using this API in RAM-constrained applications.