VESC & CAN Bus communication to another MCU (NOT VESC)

General topics and discussions about the VESC and its development.
yosoufe
Posts: 45
Joined: 18 Jul 2016, 11:26
Location: Germany

VESC & CAN Bus communication to another MCU (NOT VESC)

Postby yosoufe » 27 Jul 2016, 15:42

Hi

Is it possible to communicate with VESC using CAN bus? not to other VESC, but to other MCU to get values or set rpm and so on.
How should be the CAN protocol? in each packet of CAN, 1 byte is sent and should be treated like UART communication or it uses 8 bytes in each CAN packet or something between? Or it does not matter?

thanks

yosoufe
Posts: 45
Joined: 18 Jul 2016, 11:26
Location: Germany

Re: VESC & CAN Bus communication to another MCU (NOT VESC)

Postby yosoufe » 01 Aug 2016, 07:36

Are there any detailed data about CAN and VESC?

arvidb
Posts: 204
Joined: 26 Dec 2015, 14:38
Location: Sweden, Stockholm

Re: VESC & CAN Bus communication to another MCU (NOT VESC)

Postby arvidb » 01 Aug 2016, 14:22

I think you will have to look at the code (which of course makes up the most detailed documentation possible. ;))

In datatypes.h:

Code: Select all

// CAN commands
typedef enum {
   CAN_PACKET_SET_DUTY = 0,
   CAN_PACKET_SET_CURRENT,
   CAN_PACKET_SET_CURRENT_BRAKE,
   CAN_PACKET_SET_RPM,
   CAN_PACKET_SET_POS,
   CAN_PACKET_FILL_RX_BUFFER,
   CAN_PACKET_FILL_RX_BUFFER_LONG,
   CAN_PACKET_PROCESS_RX_BUFFER,
   CAN_PACKET_PROCESS_SHORT_BUFFER,
   CAN_PACKET_STATUS
} CAN_PACKET_ID;


CAN_PACKET_STATUS seems to return info on rpm and current, at least.

You can also see from comm_can.c that the VESC uses the CAN driver from ChibiOS to do a lot of the protocol work.

yosoufe
Posts: 45
Joined: 18 Jul 2016, 11:26
Location: Germany

Re: VESC & CAN Bus communication to another MCU (NOT VESC)

Postby yosoufe » 02 Aug 2016, 09:54

arvidb wrote:I think you will have to look at the code (which of course makes up the most detailed documentation possible. ;))

In datatypes.h:

Code: Select all

// CAN commands
typedef enum {
   CAN_PACKET_SET_DUTY = 0,
   CAN_PACKET_SET_CURRENT,
   CAN_PACKET_SET_CURRENT_BRAKE,
   CAN_PACKET_SET_RPM,
   CAN_PACKET_SET_POS,
   CAN_PACKET_FILL_RX_BUFFER,
   CAN_PACKET_FILL_RX_BUFFER_LONG,
   CAN_PACKET_PROCESS_RX_BUFFER,
   CAN_PACKET_PROCESS_SHORT_BUFFER,
   CAN_PACKET_STATUS
} CAN_PACKET_ID;


CAN_PACKET_STATUS seems to return info on rpm and current, at least.

You can also see from comm_can.c that the VESC uses the CAN driver from ChibiOS to do a lot of the protocol work.
#

Thanks. It really helped

xlv wrote:You have to use Extended addressing method.

Encode the CAN ID as this:

VESC ID = 0x01
Command CAN_PACKET_SET_RPM = 0x03 (from the enum position starting at zero)
= CAN ID -> 0x0301

To determine the content of the following 8 byte payload, look at comm_can.c at line 162

For CAN_PACKET_SET_RPM you need to send a int32 with the ERPM, so just 4 Bytes MSB


Great. Thanks.

yosoufe
Posts: 45
Joined: 18 Jul 2016, 11:26
Location: Germany

Re: VESC & CAN Bus communication to another MCU (NOT VESC)

Postby yosoufe » 02 Aug 2016, 10:34

Image

yosoufe
Posts: 45
Joined: 18 Jul 2016, 11:26
Location: Germany

Re: VESC & CAN Bus communication

Postby yosoufe » 02 Aug 2016, 17:09

Thanks for description

I made the CAN communication with my TI processor and now I can get values like the UART, without setting the VESC to send the status at 500 Hz. I get data whenever I send the request.

Thanks again

yosoufe
Posts: 45
Joined: 18 Jul 2016, 11:26
Location: Germany

Re: VESC & CAN Bus communication to another MCU (NOT VESC)

Postby yosoufe » 11 Nov 2016, 10:34

Hi

To get answer when ever you send a request via the CAN Bus, First you have to untick the sending status at 500 Hz on BLDC tool. But you have to define an can identifier for your VESC definitely.

of course you have to write your own CAN communication code to send and receive via the CAN which is dependent on your platform. For example I did it on TI Delfino processor. I called it MyCan.c. it looks like this:

Code: Select all


void CAN_init(void){
   // such as pin configuration and hardware configuration
   // setting up interrupt on each arrival of the CAN data
}

// CAN A ISR - The interrupt service routine called when a CAN interrupt is
//             triggered on CAN module A.
__interrupt void cana_rx_ISR(void){
   // save the arrival data
   // process it then via the following the function
   // I do it exactly at the arrival. not saving it on a buffer and do it later
   comm_can_process_can_buffer_inside_CANRX_ISR();
}

void comm_can_transmit(uint32_t id, uint8_t *data, uint8_t len) {
   // A function to send until 8 bytes via a can bus given an identifier
}

unsigned int CAN_available(void){ //obsolete : you do not need it anymore if you proccess the can package at its arrival
   // check if there is some data remained unprocessed in the CAN buffer
}

tCANMsgObject CAN_read (void){ //obsolete : you do not need it anymore if you proccess the can package at its arrival
   // returns the first unprocessed CAN message in the buffer (FIFO style buffer)
}


I defined a C file comm_can.c/.h, very similar to the one that Benjamin made. I just changed the function names and added a function comm_can_get_values which when I call it, my TI sends a request of get values to VESC and VESC sends the status. You can see that I commented some part of the code because they are useless on my TI processor. Do not froget. I did not change any code on the VESC. All of these codes are uploaded into my TI processor.

Code: Select all

#include "comm_can.h"
#include <string.h>

uint8_t rx_buffer[RX_BUFFER_SIZE];
unsigned int rx_buffer_last_id;
unsigned int is_comm_vesc_can_idle=1;

// Variables
can_status_msg stat_msgs[CAN_STATUS_MSGS_TO_STORE];

/*
void bldc_val_received(mc_values *val) {
   Uint32 temp_time = general_get_time();
   general_timer_freeze();
   float real_time= ((float)0xFFFFFFFF-temp_time)/200000000;
   general_timer_reset();
   general_timer_start();
   comm_can_get_values(VESC_can_ID); // to check the time for further communications
}
*/

void comm_can_init(void) {
   CAN_init();
}

// TODO implement it in more efficient way
void comm_can_process_can_buffer_inside_CANRX_ISR(void){
   int ijk;
   int32_t ind = 0;
   unsigned int rxbuf_len;
   unsigned int rxbuf_ind;
   uint8_t crc_low;
   uint8_t crc_high;
   //bool commands_send =0;

   // hetre CAN is definitley available
   // because this is executed in CAN RX interrupt

   tCANMsgObject rxmsg = sRXCANMessage;

   if ((rxmsg.ui32Flags & MSG_OBJ_EXTENDED_ID) == MSG_OBJ_EXTENDED_ID) {
      uint8_t id = rxmsg.ui32MsgID & 0xFF;
      CAN_PACKET_ID cmd = (CAN_PACKET_ID)(rxmsg.ui32MsgID >> 8);
      can_status_msg *stat_tmp;

      if (id == 255 || id == VESC_can_ID) {
         switch (cmd) {
         case CAN_PACKET_SET_DUTY:
            ind = 0;
            //mc_interface_set_duty((float)buffer_get_int32(rxmsg.pucMsgData, &ind) / 100000.0);
            break;

         case CAN_PACKET_SET_CURRENT:
            ind = 0;
            //mc_interface_set_current((float)buffer_get_int32(rxmsg.pucMsgData, &ind) / 1000.0);
            break;

         case CAN_PACKET_SET_CURRENT_BRAKE:
            ind = 0;
            //mc_interface_set_brake_current((float)buffer_get_int32(rxmsg.pucMsgData, &ind) / 1000.0);
            break;

         case CAN_PACKET_SET_RPM:
            ind = 0;
            //mc_interface_set_pid_speed((float)buffer_get_int32(rxmsg.pucMsgData, &ind));
            break;

         case CAN_PACKET_SET_POS:
            ind = 0;
            //mc_interface_set_pid_pos((float)buffer_get_int32(rxmsg.pucMsgData, &ind) / 1000000.0);
            break;

         case CAN_PACKET_FILL_RX_BUFFER:
            memcpy(rx_buffer + rxmsg.pucMsgData[0], rxmsg.pucMsgData + 1, rxmsg.ui32MsgLen - 1);
            break;

         case CAN_PACKET_FILL_RX_BUFFER_LONG:
            rxbuf_ind = (unsigned int)rxmsg.pucMsgData[0] << 8;
            rxbuf_ind |= rxmsg.pucMsgData[1];
            if (rxbuf_ind < RX_BUFFER_SIZE) {
               memcpy(rx_buffer + rxbuf_ind, rxmsg.pucMsgData + 2, rxmsg.ui32MsgLen - 2);
            }
            break;

         case CAN_PACKET_PROCESS_RX_BUFFER:
            ind = 0;
            rx_buffer_last_id = rxmsg.pucMsgData[ind++];
            rxmsg.pucMsgData[ind++];
            rxbuf_len = (unsigned int)rxmsg.pucMsgData[ind++] << 8;
            rxbuf_len |= (unsigned int)rxmsg.pucMsgData[ind++];

            if (rxbuf_len > RX_BUFFER_SIZE) {
               break;
            }

            crc_high = rxmsg.pucMsgData[ind++];
            crc_low = rxmsg.pucMsgData[ind++];

            if (crc16(rx_buffer, rxbuf_len)
                  == ((unsigned short) crc_high << 8
                        | (unsigned short) crc_low)) {

//                  if (commands_send) {
//                     send_packet_wrapper(rx_buffer, rxbuf_len);
//                  } else {
               can_process_packet(rx_buffer, rxbuf_len);
//                  }
            }
            break;

         case CAN_PACKET_PROCESS_SHORT_BUFFER:
            ind = 0;
            rx_buffer_last_id = rxmsg.pucMsgData[ind++];
            rxmsg.pucMsgData[ind++];

//               if (commands_send) {
//                  send_packet_wrapper(rxmsg.pucMsgData + ind, rxmsg.ui32MsgLen - ind);
//               } else {
            can_process_packet(rxmsg.pucMsgData + ind, rxmsg.ui32MsgLen - ind);
//               }
            break;

         case CAN_PACKET_STATUS:
            for (ijk = 0;ijk < CAN_STATUS_MSGS_TO_STORE;ijk++) {
               stat_tmp = &stat_msgs[ijk];
               if (stat_tmp->id == id || stat_tmp->id == -1) {
                  ind = 0;
                  stat_tmp->id = id;
//                     stat_tmp->rx_time = chVTGetSystemTime();
                  stat_tmp->rpm = (float)buffer_get_int32(rxmsg.pucMsgData, &ind);
                  stat_tmp->current = (float)buffer_get_int16(rxmsg.pucMsgData, &ind) / 10.0;
                  stat_tmp->duty = (float)buffer_get_int16(rxmsg.pucMsgData, &ind) / 1000.0;
                  break;
               }
            }

         default:
            break;
         }
      }
   }
}


/*
void send_packet_wrapper(unsigned char *data, unsigned int len) {
   comm_can_send_buffer(rx_buffer_last_id, data, len, true);
}
*/

void comm_can_send_buffer(uint8_t controller_id, uint8_t *data, unsigned int len, bool send) {
   uint8_t send_buffer[8];

   if (len <= 6) {
      uint32_t ind = 0;
      send_buffer[ind++] = VESC_can_ID;
      send_buffer[ind++] = send;
      memcpy(send_buffer + ind, data, len);
      ind += len;
      comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_PROCESS_SHORT_BUFFER << 8), send_buffer, ind);
   } else {
      unsigned int end_a = 0;
      unsigned int i = 0;
      for (i = 0;i < len;i += 7) {
         if (i > 255) {
            break;
         }

         end_a = i + 7;

         uint8_t send_len = 7;
         send_buffer[0] = i;

         if ((i + 7) <= len) {
            memcpy(send_buffer + 1, data + i, send_len);
         } else {
            send_len = len - i;
            memcpy(send_buffer + 1, data + i, send_len);
         }

         comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_FILL_RX_BUFFER << 8), send_buffer, send_len + 1);
      }

      for (i = end_a;i < len;i += 6) {
         uint8_t send_len = 6;
         send_buffer[0] = i >> 8;
         send_buffer[1] = i & 0xFF;

         if ((i + 6) <= len) {
            memcpy(send_buffer + 2, data + i, send_len);
         } else {
            send_len = len - i;
            memcpy(send_buffer + 2, data + i, send_len);
         }

         comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_FILL_RX_BUFFER_LONG << 8), send_buffer, send_len + 2);
      }

      uint32_t ind = 0;
      send_buffer[ind++] = VESC_can_ID;
      send_buffer[ind++] = send;
      send_buffer[ind++] = len >> 8;
      send_buffer[ind++] = len & 0xFF;
      unsigned short crc = crc16(data, len);
      send_buffer[ind++] = (uint8_t)(crc >> 8);
      send_buffer[ind++] = (uint8_t)(crc & 0xFF);

      comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_PROCESS_RX_BUFFER << 8), send_buffer, ind++);
   }
}

void can_process_packet(unsigned char *data, unsigned int len){
   bldc_interface_process_packet(data,len);
   is_comm_vesc_can_idle=1;
}


void comm_can_set_duty(uint8_t controller_id, float duty) {
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer_append_int32(buffer, (int32_t)(duty * 100000.0), &send_index);
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_SET_DUTY << 8), buffer, send_index);
}

void comm_can_set_current(uint8_t controller_id, float current) {
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer_append_int32(buffer, (int32_t)(current * 1000.0), &send_index);
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_SET_CURRENT << 8), buffer, send_index);
}

void comm_can_set_current_brake(uint8_t controller_id, float current) {
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer_append_int32(buffer, (int32_t)(current * 1000.0), &send_index);
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_SET_CURRENT_BRAKE << 8), buffer, send_index);
}

void comm_can_set_rpm(uint8_t controller_id, float rpm) {
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer_append_int32(buffer, (int32_t)rpm, &send_index);
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_SET_RPM << 8), buffer, send_index);
}

void comm_can_set_pos(uint8_t controller_id, float pos) {
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer_append_int32(buffer, (int32_t)(pos * 1000000.0), &send_index);
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_SET_POS << 8), buffer, send_index);
}

void comm_can_get_values(uint8_t controller_id) {
   is_comm_vesc_can_idle=0;
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer[send_index++] = VESC_can_ID;
   buffer[send_index++] = 0;         // make bollean send to false on VESC
   buffer[send_index++] = COMM_GET_VALUES;
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_PROCESS_SHORT_BUFFER << 8), buffer, send_index);
}

/**
 * Get status message by index.
 *
 * @param index
 * Index in the array
 *
 * @return
 * The message or 0 for an invalid index.
 */
can_status_msg *comm_can_get_status_msg_index(int index) {
   if (index < CAN_STATUS_MSGS_TO_STORE) {
      return &stat_msgs[index];
   } else {
      return 0;
   }
}

/**
 * Get status message by id.
 *
 * @param id
 * Id of the controller that sent the status message.
 *
 * @return
 * The message or 0 for an invalid id.
 */
can_status_msg *comm_can_get_status_msg_id(int id) {
   int i;
   for (i = 0;i < CAN_STATUS_MSGS_TO_STORE;i++){
      if (stat_msgs[i].id == id) {
         return &stat_msgs[i];
      }
   }

   return 0;
}


of course you will need also buffer.c, packet.c, bldc_interface.c and crc.c from benjamin. But I do not remeber how much you should change them.
Last edited by yosoufe on 17 Jun 2017, 10:25, edited 1 time in total.

skipper762
Posts: 9
Joined: 13 May 2017, 06:08

Re: VESC & CAN Bus communication to another MCU (NOT VESC)

Postby skipper762 » 04 Jun 2017, 01:56

Hi yosoufe,
I'm attempting to write an Arduino library using an MCP2515/2551 to communicate with the VESCs over CAN-BUS.
Your code has been a massive help, I have got setting the RPM, Current etc working.

I am now working to get data retrieval working, there are some parts of your code that do not make sense to me, I think it's because I'm missing the header file. In particular, I want to know about the "VESC_can_ID" variable that is used here: (and other places)

Code: Select all

void comm_can_get_values(uint8_t controller_id) {
   is_comm_vesc_can_idle=0;
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer[send_index++] = VESC_can_ID; //What is this variable?
   buffer[send_index++] = 0;         // make bollean send to false on VESC
   buffer[send_index++] = COMM_GET_VALUES;
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_PROCESS_SHORT_BUFFER << 8), buffer, send_index);
}


This variable is not defined anywhere in the code you posted.

Hopefully you can help,
Nick

yosoufe
Posts: 45
Joined: 18 Jul 2016, 11:26
Location: Germany

Re: VESC & CAN Bus communication to another MCU (NOT VESC)

Postby yosoufe » 16 Jun 2017, 23:48

skipper762 wrote:Hi yosoufe,
I'm attempting to write an Arduino library using an MCP2515/2551 to communicate with the VESCs over CAN-BUS.
Your code has been a massive help, I have got setting the RPM, Current etc working.

I am now working to get data retrieval working, there are some parts of your code that do not make sense to me, I think it's because I'm missing the header file. In particular, I want to know about the "VESC_can_ID" variable that is used here: (and other places)

Code: Select all

void comm_can_get_values(uint8_t controller_id) {
   is_comm_vesc_can_idle=0;
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer[send_index++] = VESC_can_ID; //What is this variable?
   buffer[send_index++] = 0;         // make bollean send to false on VESC
   buffer[send_index++] = COMM_GET_VALUES;
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_PROCESS_SHORT_BUFFER << 8), buffer, send_index);
}


This variable is not defined anywhere in the code you posted.

Hopefully you can help,
Nick

It is defined as macro somewhere. It should be the same as whatever you defined in bldc tools as can id (identifier). I can recheck it next week.

yosoufe
Posts: 45
Joined: 18 Jul 2016, 11:26
Location: Germany

Re: VESC & CAN Bus communication to another MCU (NOT VESC)

Postby yosoufe » 22 Jun 2017, 09:01

skipper762 wrote:Hi yosoufe,
I'm attempting to write an Arduino library using an MCP2515/2551 to communicate with the VESCs over CAN-BUS.
Your code has been a massive help, I have got setting the RPM, Current etc working.

I am now working to get data retrieval working, there are some parts of your code that do not make sense to me, I think it's because I'm missing the header file. In particular, I want to know about the "VESC_can_ID" variable that is used here: (and other places)

Code: Select all

void comm_can_get_values(uint8_t controller_id) {
   is_comm_vesc_can_idle=0;
   int32_t send_index = 0;
   uint8_t buffer[4];
   buffer[send_index++] = VESC_can_ID; //What is this variable?
   buffer[send_index++] = 0;         // make bollean send to false on VESC
   buffer[send_index++] = COMM_GET_VALUES;
   comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_PROCESS_SHORT_BUFFER << 8), buffer, send_index);
}


This variable is not defined anywhere in the code you posted.

Hopefully you can help,
Nick


Yes. It is defined as a macro. it may be changed to

Code: Select all

buffer[send_index++] = controller_id;


Return to “General”

Who is online

Users browsing this forum: No registered users and 2 guests