2
\$\begingroup\$

I need help to send a double value using a CAN Bus.

I am working on a eletronic traction control and i need to send, by a CAN bus, the calculated values of speed of wheels. The problem start when the calculated value is a double type and i need to send it as a short int or int.

Example:

double speed_calculated;

short  value_to_send;

char can_data_to_send[8]; //the function CANWrite sends a vector of char;

speed_calculated = function_to_calculate_correction();

value_to_send = (short)speed_calculated * 10;

can_data_to_send[0] = value_to_send;

can_data_to_send[1] = value_to_send >> 8;

My intention to do is:

If the "speed_calculated" is 10.5, so multiply by 10 and storage 105 in "value_to_send". As "value_to_send" is a short type, theoretically, i can send 2 bytes.

But, as i am using arduino to test and debug the CAN communication, i am not receiving the correct values.

If i remove the multiplier "*10" of "value_to_send = (short)speed_calculated" i receive only the value of 98 instead 198 that is the correct.

I don't know what i am doing wrong, i've been spending much time searching and testing differents ideas but no one works.

I appreciate the help

\$\endgroup\$
6
  • \$\begingroup\$ Does the CANWrite() always send 8 bytes? (I don't know. I'm asking.) If not 8, how does it know how many bytes to transmit? If always 8, what are the other values you are sending along? You might consider writing ((short) floor(speed_calculated*10.0 + 0.5)) instead of what you write. That will perform the calculation in FP with results in FP, just before truncating to fit the short int. Also be aware that a short int is signed, not unsigned. Just FYI. You may want to use an unsigned short int, instead. And while I'm at it, make your CAN buffer an unsigned char array, if allowed. \$\endgroup\$
    – jonk
    Commented Nov 10, 2019 at 3:45
  • \$\begingroup\$ Casting from signed values to unsigned values is always defined in C (using a modulo operator to do so.) Casting from unsigned to signed is NOT always defined in C. (Or wasn't with C99, memory serving -- see Harbison & Steele, 5th edition.) Your binary right-shift operations will always make sense on unsigned values. They will not always make sense on signed values (this is another area where C permits "whatever the machine architecture makes easier to do" when dealing with signed values.) These were "freedoms" allowed to compiler writers', back when I was writing C compilers decades ago. \$\endgroup\$
    – jonk
    Commented Nov 10, 2019 at 3:49
  • 1
    \$\begingroup\$ also, recommend using int16_t rather than short, using stdint.h, to be clearer about intended bits and signedness in the declared datatypes. \$\endgroup\$
    – vicatcu
    Commented Nov 10, 2019 at 4:46
  • 2
    \$\begingroup\$ The cast at value_to_send = (short)speed_calculated * 10; will not save the fractional part. You need parentheses: value_to_send = (short)(speed_calculated * 10);. \$\endgroup\$ Commented Nov 10, 2019 at 17:06
  • 1
    \$\begingroup\$ Consider dropping floating point altogether. You can as well work with 10 times bigger integers - it will only be faster and in this case more accurate than double. Using floating point on 8 bitters like Arduino is a big no-no, since they don't have a FPU but have to resolve these in painfully slow and bloated software floating point libs. \$\endgroup\$
    – Lundin
    Commented Nov 11, 2019 at 9:57

2 Answers 2

2
\$\begingroup\$

To avoid side-effects, a better way would be to use this statement

value_to_send = (short)((double)speed_calculated * (double)10.0);

Refer -> PromotionRules

As to the second part of your question, you say 198 is the expected value. Which means, function_to_calculate_correction() must return 198(0xC6) .

I see no way how short to char conversion would lead 198 to be loaded as 98 in can_data_to_send[0] as 198 can be easily accommodated in a byte.

Also, if your scaling factor is 10, is double really required?

\$\endgroup\$
5
  • \$\begingroup\$ There is no need for both the casts to (double) inside the parentheses. speed_calculated is already a double and 10.0 will be implicitely casted to this "broader" type. You could even write 10 and it will be automatically casted. \$\endgroup\$ Commented Nov 28, 2019 at 8:08
  • 1
    \$\begingroup\$ I have the habit of always writing what i mean and leave none to assumptions. Force of habit with no performance improvement. \$\endgroup\$
    – AlphaGoku
    Commented Nov 28, 2019 at 10:02
  • 1
    \$\begingroup\$ Well, first it distracted me from the code doing something, and second I was wondering why you cast, so it is misleading, at least for me. ;-) Do you also write bool isOk; /* some code to set isOk */ if (true == isOk) { /* ... */ }? :-D \$\endgroup\$ Commented Nov 28, 2019 at 10:21
  • 1
    \$\begingroup\$ Readability.. Code should speak for itself. \$\endgroup\$
    – AlphaGoku
    Commented Jan 13, 2020 at 0:48
  • \$\begingroup\$ At least for me, in this case readability is worse. The casts distract from the operation, they are so much heavier. \$\endgroup\$ Commented Jan 13, 2020 at 6:45
0
\$\begingroup\$

i have made 2 converters to just simply convert an double to singles to send using any canbus library and then on the other arduino reconvert back my own can message:

struct can_msg{
__u8 id;
__u8 dlc;
__u16 data[8];
};

the can library message mcp2515 library:

struct can_frame {
__u32 can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
__u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
__u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};

the functions:

can_msg dataParseTo4M(can_frame x){
struct can_msg y;
y.data[0] = y.data[0] | x.data[0];
y.data[0] = y.data[0] << 8;
y.data[0] = y.data[0] | x.data[1];
y.data[1] = y.data[1] | x.data[2];
y.data[1] = y.data[1] << 8;
y.data[1] = y.data[1] | x.data[3];
y.data[2] = y.data[2] | x.data[4];
y.data[2] = y.data[2] << 8;
y.data[2] = y.data[2] | x.data[5];
y.data[3] = y.data[3] | x.data[6];
y.data[3] = y.data[3] << 8;
y.data[3] = y.data[3] | x.data[7];
return y;
}
can_msg dataParseTo8M(can_frame x){
struct can_msg y;

for(__u8 i=0 ; i<8; i++){
    y.data[i] = x.data[i];
}
return y;
}   
can_frame dataParse4To8M(can_msg y){
struct can_frame x;
x.data[1] = y.data[0];
x.data[0] = y.data[0] >> 8;
x.data[3] = y.data[1];
x.data[2] = y.data[1] >> 8;
x.data[5] = y.data[2];
x.data[4] = y.data[2] >> 8;
x.data[7] = y.data[3];
x.data[6] = y.data[3] >> 8;

  return x;

}
can_frame dataParse8To8M(can_msg y ){
struct can_frame x;

for(__u8 i=0 ; i<8; i++){
    x.data[i] = y.data[i];
}
return x;
}

can_msg Extract(can_frame x){
struct can_msg y;
switch(x.can_id){
    case 0xC0:
    case 0xC1:
    case 0xC2:
    case 0xC3:
            y = dataParseTo4M(x);
            y.id = x.can_id;
            y.dlc = x.can_dlc;
            break;
    case 0xE0:
    case 0xE1:
    case 0xE2:
    case 0xE3:
            y = dataParseTo8M(x);
            y.id = x.can_id;
            y.dlc = x.can_dlc;
            break; 
    default:
            y = dataParseTo8M(x);
            y.id = x.can_id;
            y.dlc = x.can_dlc;
            break; 
}
return y;
}
can_frame Convert(__u32 id, __u8 dlc, __u16 d0, __u16 d1,__u16 d2,__u16 d3,__u16 d4,__u16 d5,__u16 d6,__u16 d7){
struct can_msg y;
y.data[0] = d0;y.data[1] = d1;y.data[2] = d2;y.data[3] = d3;
y.data[4] = d4;y.data[5] = d5;y.data[6] = d6;y.data[7] = d7;

struct can_frame x;

switch(id){
    case 0xC0:
    case 0xC1:
    case 0xC2:
    case 0xC3:
        x = dataParse4To8M(y);
        x.can_id = id;
        x.can_dlc = dlc;
    break;
    case 0xE0: 
    case 0xE1:
    case 0xE2:
    case 0xE3:
        x = dataParse8To8M(y);
        x.can_id = id;
        x.can_dlc = dlc;
    break; 
    default:
        x = dataParse8To8M(y);
        x.can_id = id;
        x.can_dlc = dlc;
        //Serial.println(x.can_id,HEX);
    break; 
}
return x;
}

edit: feel like i need to add this :

canframe = Convert(0xC0,6,ID,CellVoltage[0]*0.4882,CellVoltage[1]*0.4882,0,0,0,0,0);

mcp2515.sendMessage(&canframe);

and the other way back

if (mcp2515.readMessage(&canframe) == MCP2515::ERROR_OK) {
        canMsg = Extract(canframe);
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.