Search code examples
pythoncarmcan-bus

Signal based CAN bus frame manipulation in C


I'm trying to manipulate a CAN signal using C (with a PCAN Router interface).

What the program does is waits for a CAN specific frame like 0x1F0 and then modify some bits. The bits I want to modify is based on a DBC file. So im trying to make it more dynamically by using cantools to generate a source code a .h & .c file based on a DBC file here it's an example motohawk.h and motohawk.c.

Link to the motor DBC file.

What I wish is to be able to manipulate a signal somehow like this.

So the question is how do I use the source to manipulate a signal signal? for example the Temperature signal? Maybe I'm seeing it wrong and it's not possible to do what I wish to.

Frame from DBC:

BO_ 496 ExampleMessage: 8 PCM1
 SG_ Temperature : 0|12@0- (0.01,250) [229.52|270.47] "degK"  PCM1,FOO
 SG_ AverageRadius : 6|6@0+ (0.1,0) [0|5] "m" Vector__XXX
 SG_ Enable : 7|1@0+ (1,0) [0|0] "-" Vector__XXX

What I want to do is to have a struct similar to this:

struct motohawk_example_message_t {
    /**
     * Range: -
     * Scale: 1
     * Offset: 0
     */
    uint8_t enable;

    /**
     * Range: 0..50 (0..5 m)
     * Scale: 0.1
     * Offset: 0
     */
    uint8_t average_radius;

    /**
     * Range: -2048..2047 (229.52..270.47 degK)
     * Scale: 0.01
     * Offset: 250
     */
    int16_t temperature;

};

but I would like to have a possibility to use something like:

frame_1F0 = motohawk_example_message_t();
frame_1F0.temperature.value = 255.01;

the frame_1F0.temperature.value will populate the correct bit/bytes in the frame (in the PCAN is something like TxMsg.Data16[index] = some hex value). Has somebody done it? or do you know if it's possible to use the source generated by the cantools package to do it?

Here is the PCAN code

frame_1F0 = motohawk_example_message_t();

while (1)
    {
        CANMsg_t  RxMsg, TxMsg;
        
        
        // process messages from CAN1
        if ( CAN_UserRead ( CAN_BUS1, &RxMsg) != 0)
        {
            // message received from CAN1
            // catch ID 1F0h from CAN1 to modify data
            if ( RxMsg.Id == 0x1F0)
            {
                RxMsg.Id = 0x1E4;
                RxMsg.Type = CAN_MSG_STANDARD;
            }           

            // copy message
            TxMsg.Id   = RxMsg.Id;
            TxMsg.Type = RxMsg.Type;
            TxMsg.Len  = RxMsg.Len;
            frame_1F0.temperature.value = 255.01; // this should be TxMsg.Data[xxx] = 255.01. THIS is the hard part :/ 
            

            // send
            CAN_UserWrite ( CAN_BUS2, &TxMsg);
...

Solution

  • The code generated with cantools can be used to convert the binary data to structs where you can access the signals and vice versa.

    In your case something like this should work

    ...
    CANMsg_t  RxMsg, TxMsg;
    struct motohawk_example_message_t frame_1F0;
    
    motohawk_example_message_unpack(&frame_1F0, RxMsg.Data.Data8, RxMsg.Len);
    
    int16_t encoded_temperature = motohawk_example_message_temperature_encode(255.01);
    
    frame_1F0.temperature.value = encoded_temperature;
    
    motohawk_example_message_pack(TxMsg.Data.Data8, &frame_1F0, TxMsg.Len);
    
    ...
    

    Typing this of the top of my head. The code may not compile out-of-the-box but I hope it helps to get the idea.


    In the generated source code, the _pack and _unpack functions can be used to convert from _message struct to binary data and back.

    The _encode and _decode functions are used to convert the signals from their physical representation to their raw representation and back.