Skip to content

Integrating CAN message into your code

Generating CAN message headers

There is a python script located in the can/ folder called generate_can_headers.py. This script takes a DBC file, and turns it into a set of macros, structs, and enums.

Usage guide:

python [path to python script]/generate_can_headers.py [path to dbc file]

Usage example (called from the root directory of Embedded-Sharepoint):

python can/generate_can_headers.py can/dbc/Mcqueen/CarCAN.dbc

What's included in the header

The generated header file includes macros for every CAN_ID

/* ================= CAN ID Macros ================= */

#define CAN_ID_VCU_STATUS 0x10
#define CAN_ID_CONTROLS_STATUS 0x15

The generated header file includes macros for every CAN message size. This is a useful field to use when creating CAN transmit or CAN recieve header files.

/* ================= CAN Length Macros ================= */

#define CAN_DLC_VCU_STATUS 3
#define CAN_DLC_CONTROLS_STATUS 4

The generated header file includes enums for every CAN value tables, this is useful for encoding and decoding CAN messages without using magic numbers.

/* ================= Value Table Enums ================= */

typedef enum {
    VCU_STATUS_VCU_FAULT_MOTOR_HV_UNDERVOLTAGE = 6,
    VCU_STATUS_VCU_FAULT_MOTOR_HV_OVERVOLTAGE = 5,
    VCU_STATUS_VCU_FAULT_MOTOR_CONTROLLER_FAULT = 4,
    VCU_STATUS_VCU_FAULT_MOTOR_PRECHARGE_TIMEOUT = 3,
    VCU_STATUS_VCU_FAULT_MOTOR_PCHG_CONTACTOR_SENSE = 2,
    VCU_STATUS_VCU_FAULT_MOTOR_CONTACTOR_SENSE = 1,
    VCU_STATUS_VCU_FAULT_NO_FAULT = 0,
} vcu_status_vcu_fault_e;

The generated header file includes structs for every CAN message, this is useful for storing CAN data in a more readable format than an array of bytes.

/* ================= Message Structs ================= */

typedef struct {
    uint8_t VCU_Fault;
    uint8_t Motor_Contactor_State;
    uint8_t Motor_Precharge_Contactor_State;
    uint8_t Motor_Ready_To_Drive;
    uint8_t VCU_Driver_Input_OK;
    uint8_t VCU_Pedals_OK;
    uint8_t VCU_Regen_OK;
    uint8_t VCU_Regen_Active;
    uint8_t VCU_FSM_State;
} vcu_status_t;

Writting packing functions

To maintain more modular code, it's good practice to write a function to "pack" a can message struct into an array of bytes. For example,


// encodes a drive command struct into an array of bytes for can_send
static void packDriveCommand(mc_drivecommand_t motorDriveCommand, uint8_t tx_data[8]){
    memcpy(&tx_data[4], &(motorDriveCommand.MC_MotorCurrentSetpoint), sizeof(float));
    memcpy(&tx_data[0], &(motorDriveCommand.MC_MotorVelocitySetpoint), sizeof(float));
}

Data from the motorDriveCommand can message struct is packed into the tx_data array of bytes to send over CAN.

This reverse can also be done on a recieved can message to "unpack" an array of bytes into a can message struct.