-
Notifications
You must be signed in to change notification settings - Fork 2
2. FRC CAN Device Specifications
Before reading this article, please read the official FRC CAN Device Specifications on WPILib Docs.
https://docs.wpilib.org/en/stable/docs/software/can-devices/can-addressing.html
The 'CAN ID' we talked about when setting up motor controllers (Spark, Kraken, PDP, etc) is a number from 0-63. This is not the CAN identifier (message ID) used in CAN data layer. FRC Use a very specific scheme outlined in FRC CAN specification to convert certain information to CAN identifier. All device connected to roboRIO must follow this scheme when determine what ID to send data to and read form.
CAN identifier is calculated from device Type ID, manufacturer ID, API ID (in the documentation it talked about API Class and Index but doesn't matter and it is just a suggestion. Each device will have its unique API ID designed for its use) and device number (0-63 as the traditional 'CAN ID' we talked about in FRC).
You must generate your CAN identifier (message ID) using this very specific way. A device will have multiple CAN message sent to and from roboRIO (and possibly to other non-roboRIO device). These different messages will share the same device type ID, manufacturer ID and device number. API ID will change based on use.
// === CAN Constants ===
#define DEVICE_TYPE_ID 0x0A //DONOT CHANGE
#define MANUFACTURER_ID 0x08 //DONOT CHANGE
#define DEVICE_NUMBER 33 // Device Number 0-63
#define STATUS_API_ID 0x180
#define COLOR_SENSOR_API_ID 0x184
#define CONTROL_API_ID 0x190
#define HEARTBEAT_ID 0x01011840
uint32_t makeCANMsgID(uint8_t deviceTypeID, uint8_t manufacturerID, uint16_t apiID, uint8_t deviceNumber) {
return ((uint32_t)(deviceTypeID & 0xFF) << 24) |
((uint32_t)(manufacturerID & 0xFF) << 16) |
((uint32_t)(apiID & 0x3FF) << 6) |
(deviceNumber & 0x3F);
}struct DecodedCANID {
uint8_t deviceTypeID;
uint8_t manufacturerID;
uint16_t apiID;
uint8_t deviceNumber;
};
DecodedCANID decodeCANMsgID(uint32_t can_id) {
DecodedCANID decoded;
decoded.deviceTypeID = (can_id >> 24) & 0xFF;
decoded.manufacturerID = (can_id >> 16) & 0xFF;
decoded.apiID = (can_id >> 6) & 0x3FF;
decoded.deviceNumber = can_id & 0x3F;
return decoded;
}
uint32_t msgID = makeCANMsgID(0x0A, 0x08, 0x184, 33);
DecodedCANID result = decodeCANMsgID(msgID);
Serial.printf("Device Type ID: 0x%02X\n", result.deviceTypeID);
Serial.printf("Manufacturer ID: 0x%02X\n", result.manufacturerID);
Serial.printf("API ID: 0x%03X\n", result.apiID);
Serial.printf("Device Number: %d\n", result.deviceNumber);Upon robot code startup (code go green on Driver Station), roboRIO will send a universal heartbeat on CAN message ID 0x01011840.
Per FRC Game manual R714, you should not send any CAN data packet on the above message ID.
In FRC documentation and tooling, the phrase “CAN ID” is overloaded and often used incorrectly. This is the root of most confusion for newcomers.
There are three different concepts that people casually call “CAN ID”:
- CAN Device Number (what REV / CTRE tools configure)
- API ID (what kind of message this is)
- CAN Message ID (the actual 29-bit identifier on the wire)
Only #1 is user-configurable in vendor tools.
When you open REV Hardware Client or CTRE Phoenix Tuner and set a device to “CAN ID = 33”, you are not setting a CAN message ID.
You are setting the CAN Device Number:
#define DEVICE_NUMBER 33 // valid range: 0–63This value:
- Identifies which physical device you are talking to
- Is appended into every CAN message sent to or from that device
- Is not unique by itself on the bus (manufacturer + device type matter)
Think of this as a node address, not a message identifier.
The API ID describes what kind of message is being sent:
#define STATUS_API_ID 0x180
#define COLOR_SENSOR_API_ID 0x184
#define CONTROL_API_ID 0x190Examples:
-
0x180→ status / telemetry -
0x190→ control command -
0x184→ sensor data
API IDs are defined by:
- WPILib HAL
- Vendor firmware (REV / CTRE)
- Your own custom device firmware
Users do not set API IDs in vendor software. API IDs are fixed in firmware and are only created by the vendor or by developers building custom CAN devices who design their own APIs based on their device’s functionality.
This is the 29-bit extended CAN identifier that the controller transmits.
In FRC, the message ID is a packed structure:
[ device type ][ manufacturer ][ api id ][ device number ]
Your helper function shows this clearly:
uint32_t makeCANMsgID(uint8_t deviceTypeID,
uint8_t manufacturerID,
uint16_t apiID,
uint8_t deviceNumber) {
return ((uint32_t)(deviceTypeID & 0xFF) << 24) |
((uint32_t)(manufacturerID & 0xFF) << 16) |
((uint32_t)(apiID & 0x3FF) << 6) |
(deviceNumber & 0x3F);
}This means:
| Field | Bits | Purpose |
|---|---|---|
deviceTypeID |
5 | Device type (motor, sensor, etc.) |
manufacturerID |
8 | Vendor or custom device |
apiID |
10 | Message meaning |
deviceNumber |
6 | Physical device number |
The CAN Message ID is computed, not assigned.
Vendor software labels this field as “CAN ID”, but what they really mean is:
“Set the device number portion of the CAN message ID”
They hide:
- Device type
- Manufacturer ID
- API ID
Because those are fixed by firmware.
So when someone says:
“Set the CAN ID to 33”
What they really mean is:
“Set the CAN Device Number to 33”
When you build your own ESP32 / Arduino CAN device:
-
You must generate the full CAN Message ID yourself
-
The roboRIO will only listen if:
- Manufacturer ID matches
- API ID matches
- Device number matches
If any part is wrong:
- Messages will silently disappear
- No error will be reported
- The bus might still look “healthy”
This is why understanding the difference is critical for debugging.
CAN Device Number ≠ CAN Message ID
- Device Number → user-assigned node address (0–63)
- API ID → message type
- CAN Message ID → packed identifier used on the wire
Once you separate these concepts, FRC CAN suddenly makes a lot more sense.