VESC - Custom Applications

vedder

100 W
Joined
Nov 5, 2011
Messages
249
I wrote a short tutorial a few days ago about how to write and run custom applications for the VESC:
http://vedder.se/2015/08/vesc-writing-custom-applications/

As you can see, it does not take many lines to do something useful. Would anyone like to give this a try? If there is anything you have questions about in the code I will try my best to help. For example, you can use the PWM input from the servo_dec driver and write your custom application with e.g. smoothing and maybe add some brake lights using the ws2812 driver.

Regarding UART communication, I will write a library for that that can be used from any mcu that can be programmed in C and write a post about that. It would be nice if someone could give that a try on e.g. arduino.
 
Hi Benjamin,

I've written a simple Arduino library (attached) to send commands to VESC over UART. Hopefully it can be useful to other people who try VESC, in addition to the c library that you are gonna write. I am using it on Arduino Due but it should work on all other Arduions too (knock on wood). All the necessary code was extracted with some simplifications from the BLDC Tool source.

The library can send six commands:
Code:
void SetCurrent(double val)   //Current control
  void SetRpm(double val)   //RPM control
  void SetDuty(double val)   //Set duty cycle
  void SetBrake(double val)   //Set brake current
  void Release()   //Release the motor
  void FullBrake()   //Short motor phases for max braking

To compile with the library, the header file should be placed into the folder .../Arduino/libraries/VescController/

Then the usage is like this:

Code:
#include "VescController.h"

//Declare a controller object and tell it to use Serial1 port to communicate with VESC.
//Other serial ports on Arduino are called Serial, Serial2 and Serial3
//On some arduinos, like Arduino Nano, only Serial is available

VescController vesc(Serial1);

void setup
{
  Serial1.begin(115200);
}

void loop
{
   vesc.SetCurrent(3.00);
   delay(2000);

   vesc.SetRpm(7000);
   delay(2000);

   vesc.SetDuty(0.2);
   delay(2000);

   vesc.SetBrake(3.0);
   delay(2000);

   vesc.Release();
   delay(2000);

   void FullBrake();
   delay(2000);
}
 

Attachments

  • VescController.h
    4.5 KB · Views: 655
Benjamin said:
Regarding UART communication, I will write a library for that that can be used from any mcu that can be programmed in C and write a post about that. It would be nice if someone could give that a try on e.g. arduino.
C sounds good, please avoid using Qt for such example, it is too large of a dependency.

pkondratyuk said:
Hi Benjamin,

I've written a simple Arduino library (attached) to send commands to VESC over UART.
What are all the UART pin connections exactly on the VESC side and Arduino side? Some picture that shows the connections on both sides would be great.
 
@erwincoumans:

What are all the UART pin connections exactly on the VESC side and Arduino side?

On the Arduino side, it's simple - they are marked RX, TX and GND. If there are more serial ports, they are RX1, TX1, RX2, TX2 etc. On the VESC side the UART port is on the PWR_COMM connector. See this schematic from Vedder's site:

http://vedder.se/wp-content/uploads/2015/01/Schematic-1.png and
http://vedder.se/wp-content/uploads/2015/01/PCB_Front.png

The UART pins on the PWR_COMM connector are 1, 2 and 4 (RX, TX and GND). Numbering on that connector starts from the side where the motor wires are soldered. You need to connect Arduino GND to VESC GND, Arduino RX to VESC TX, and Arduino TX to VESC RX.

Actually, if you are only sending commands from Arduino to VESC, you don't need Arduino RX to VESC TX connection. So it's only two wires in that case.
 
vedder said:
Would anyone like to give this a try?
Yes, I am experimenting with it, thanks for the info!

By the way, how does the UDP feature in BLDC tool work? Do we need to solder a network connector on VESC?
It would be nice if someone could give that a try on e.g. arduino.

Yes, it works on Arduino Uno, using Serial instead of Serial1. It would be nice to add the option to receive status data, is that easy to add?

Thanks!
 
Once you have a few VESC's it is fun to control them externally over CAN-bus. Such motion control over CAN could be useful for multi-wheel vehicles using VESC. Another application would be to perform Cartesian motion control for a robot, but that is outside of the scope of this forum.

I bought a cheap CAN-BUS shield for Arduino, and multiple VESCs can be controlled, pretty cool.
http://www.amazon.com/gp/product/B00NQVH666?psc=1&redirect=true&ref_=oh_aui_detailpage_o01_s00

This is the shield, and below is the script. It requires the CAN_BUS_Shield Arduino library to be installed (following the instructions here: http://www.seeedstudio.com/wiki/CAN-BUS_Shield).
It would be easy to support other commands too.

Code:
comm_can_set_rpm(CAN_target_ID,electrical rpm);

FullSizeRender-9a.jpg

The script sets the target eRPM to 4000 for 0.1 second to VESC with CAN ID 1 and a VESC with CAN ID 5. After that it will set the eRPM for both VESCs to 0, then -4000 eRPM for 0.1 second and then it sets the RPM to 0 again (for a small test).

Code:
// demo: CAN-BUS Shield, receive data with interrupt mode
// when in interrupt mode, the data coming can't be too fast, must >20ms, or else you can use check mode
// loovee, 2014-6-13

#include <SPI.h>
#include "mcp_can.h"

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin


unsigned char flagRecv = 0;
unsigned char len = 0;
unsigned char buf[8];
char str[20];

void buffer_append_int16(uint8_t* buffer, int16_t number, int32_t *index) {
        buffer[(*index)++] = number >> 8;
        buffer[(*index)++] = number;
}

void buffer_append_uint16(uint8_t* buffer, uint16_t number, int32_t *index) {
        buffer[(*index)++] = number >> 8;
        buffer[(*index)++] = number;
}

void buffer_append_int32(uint8_t* buffer, int32_t number, int32_t *index) {
        buffer[(*index)++] = number >> 24;
        buffer[(*index)++] = number >> 16;
        buffer[(*index)++] = number >> 8;
        buffer[(*index)++] = number;
}

void buffer_append_uint32(uint8_t* buffer, uint32_t number, int32_t *index) {
        buffer[(*index)++] = number >> 24;
        buffer[(*index)++] = number >> 16;
        buffer[(*index)++] = number >> 8;
        buffer[(*index)++] = number;
}

// 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;



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);

        CAN.sendMsgBuf(controller_id | ((uint32_t)CAN_PACKET_SET_RPM << 8), 1, send_index, buffer);
//        comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_SET_RPM << 8), buffer, send_index);
}


void setup()
{
  
    Serial.begin(115200);

START_INIT:

    if(CAN_OK == CAN.begin(CAN_500KBPS))                   // init can bus : baudrate = 500k
    {
        Serial.println("CAN BUS Shield init ok!");
    }
    else
    {
        Serial.println("CAN BUS Shield init fail");
        Serial.println("Init CAN BUS Shield again");
        delay(100);
        goto START_INIT;
    }

    attachInterrupt(0, MCP2515_ISR, FALLING); // start interrupt
    
      comm_can_set_rpm(1,4000);
      comm_can_set_rpm(5,4000);
      delay(100);

      comm_can_set_rpm(1,0000);
      comm_can_set_rpm(5,0000);
    delay(1000);

     comm_can_set_rpm(1,-4000);
      comm_can_set_rpm(5,-4000);

    delay(100);
      comm_can_set_rpm(1,0000);
      comm_can_set_rpm(5,0000);

}

void MCP2515_ISR()
{
    flagRecv = 1;
}

void loop()
{
  
    if(flagRecv) 
    {                                   // check if get data

        flagRecv = 0;                   // clear flag

        // iterate over all pending messages
        // If either the bus is saturated or the MCU is busy,
        // both RX buffers may be in use and reading a single
        // message does not clear the IRQ conditon.
        while (CAN_MSGAVAIL == CAN.checkReceive()) 
        {
          
           
            // read data,  len: data length, buf: data buf
            CAN.readMsgBuf(&len, buf);

           Serial.print("Get Data From id: ");
           Serial.println(CAN.getCanId());
           if (CAN.isExtendedFrame())
           {
             Serial.println("IsExtendedFrame");
           } else
           {
            Serial.println("Is NOT ExtendedFrame");
       }
        
          // print the data
            for(int i = 0; i<len; i++)
            {
                Serial.print(buf[i]);Serial.print("\t");
            }
            Serial.println();
        }
    }
}

/*********************************************************************************************************
  END FILE
*********************************************************************************************************/
 
pkondratyuk said:
Hi Benjamin,

I've written a simple Arduino library (attached) to send commands to VESC over UART. Hopefully it can be useful to other people who try VESC...

Hi pkondratyuk.

Firstly - thanks for the Arduino library you wrote. I got it working nicely, and modified it to work with SoftwareSerial as well. This allows me to control VESC over Bluetooth 8)

I'm trying to get values back from VESC as well, using COMM_GET_VALUES. I've dug through the video logger code from Vedder, and your code, and I can't see any reason why it shouldn't work....I literally just send the COMM_GET_VALUES value (4, as per packetinterface.cpp) along with the CRC bits, but I never get any serial data back.

I don't suppose you've tried or had any luck reading back VESC data over UART have you? I'm happy to share my code once it's tidy :)
 
Hey benj,

I have not tried requesting data from VESC yet, though I plan to do that. I'll let you know when I get it working. If you figure it out before I get to it, could you make a post about it here, too?
 
I've also started trying to get data back over CAN bus to Arduino.
Has anyone made any more progress on this yet ?
 
Hi.
How to interface an arduino with the vesc to send commands with tx?
Do i need to change something ?
 
erwincoumans said:
Once you have a few VESC's it is fun to control them externally over CAN-bus. Such motion control over CAN could be useful for multi-wheel vehicles using VESC. Another application would be to perform Cartesian motion control for a robot, but that is outside of the scope of this forum.

The script sets the target eRPM to 4000 for 0.1 second to VESC with CAN ID 1 and a VESC with CAN ID 5. After that it will set the eRPM for both VESCs to 0, then -4000 eRPM for 0.1 second and then it sets the RPM to 0 again (for a small test).

Code:
// demo: CAN-BUS Shield, receive data with interrupt mode
// when in interrupt mode, the data coming can't be too fast, must >20ms, or else you can use check mode
// loovee, 2014-6-13

#include <SPI.h>
#include "mcp_can.h"

// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin


unsigned char flagRecv = 0;
unsigned char len = 0;
unsigned char buf[8];
char str[20];

void buffer_append_int16(uint8_t* buffer, int16_t number, int32_t *index) {
        buffer[(*index)++] = number >> 8;
        buffer[(*index)++] = number;
}

void buffer_append_uint16(uint8_t* buffer, uint16_t number, int32_t *index) {
        buffer[(*index)++] = number >> 8;
        buffer[(*index)++] = number;
}

void buffer_append_int32(uint8_t* buffer, int32_t number, int32_t *index) {
        buffer[(*index)++] = number >> 24;
        buffer[(*index)++] = number >> 16;
        buffer[(*index)++] = number >> 8;
        buffer[(*index)++] = number;
}

void buffer_append_uint32(uint8_t* buffer, uint32_t number, int32_t *index) {
        buffer[(*index)++] = number >> 24;
        buffer[(*index)++] = number >> 16;
        buffer[(*index)++] = number >> 8;
        buffer[(*index)++] = number;
}

// 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;



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);

        CAN.sendMsgBuf(controller_id | ((uint32_t)CAN_PACKET_SET_RPM << 8), 1, send_index, buffer);
//        comm_can_transmit(controller_id | ((uint32_t)CAN_PACKET_SET_RPM << 8), buffer, send_index);
}


void setup()
{
  
    Serial.begin(115200);

START_INIT:

    if(CAN_OK == CAN.begin(CAN_500KBPS))                   // init can bus : baudrate = 500k
    {
        Serial.println("CAN BUS Shield init ok!");
    }
    else
    {
        Serial.println("CAN BUS Shield init fail");
        Serial.println("Init CAN BUS Shield again");
        delay(100);
        goto START_INIT;
    }

    attachInterrupt(0, MCP2515_ISR, FALLING); // start interrupt
    
      comm_can_set_rpm(1,4000);
      comm_can_set_rpm(5,4000);
      delay(100);

      comm_can_set_rpm(1,0000);
      comm_can_set_rpm(5,0000);
    delay(1000);

     comm_can_set_rpm(1,-4000);
      comm_can_set_rpm(5,-4000);

    delay(100);
      comm_can_set_rpm(1,0000);
      comm_can_set_rpm(5,0000);

}

void MCP2515_ISR()
{
    flagRecv = 1;
}

void loop()
{
  
    if(flagRecv) 
    {                                   // check if get data

        flagRecv = 0;                   // clear flag

        // iterate over all pending messages
        // If either the bus is saturated or the MCU is busy,
        // both RX buffers may be in use and reading a single
        // message does not clear the IRQ conditon.
        while (CAN_MSGAVAIL == CAN.checkReceive()) 
        {
          
           
            // read data,  len: data length, buf: data buf
            CAN.readMsgBuf(&len, buf);

           Serial.print("Get Data From id: ");
           Serial.println(CAN.getCanId());
           if (CAN.isExtendedFrame())
           {
             Serial.println("IsExtendedFrame");
           } else
           {
            Serial.println("Is NOT ExtendedFrame");
       }
        
          // print the data
            for(int i = 0; i<len; i++)
            {
                Serial.print(buf[i]);Serial.print("\t");
            }
            Serial.println();
        }
    }
}

/*********************************************************************************************************
  END FILE
*********************************************************************************************************/

I'm trying to reproduce your setup and failing. Perhaps you can help ?
What settings do you need in the VESC apart from their ID ?

I also don't understand your code.
The documentation says that the id in the instruction
Code:
 CAN.sendMsgBuf(INT8U id, INT8U ext, INT8U len, data_buf)
is the node where the data came from, that's the source , not the target.
It appears your using it as the target ?
Am I mistaken ? Or is the documentation worded wrong ?

Thanks.
 
hello, can i use nunchuck and arduino on the UART port on the same time? Basically i want to control the VESC using the wireless controller and read data (with arduino) from VESC( voltage, amps, etc) and send them to my mobile phone over bluetooth?
 
Hi there,

Luckily, I have found this thread… since weeks I am thinking about a new vehicle – I would like to build.
Basically it will be a beer-box on wheels  https://www.youtube.com/watch?v=7RUAW5s0ttU
Instead of the combustion-engine, I would like to realize a 4x4 bldc set up. (Pmax/motor about 4kW)
 As there is not much space, I would avoid a mechanical brake and use the motors instead
I will have four times:
• Wheel (longboard)
• Belt
• Motor (outrunner bldc about 200kV and BLDC)
• Controller (vesc6?)
Imput to Arduino:
• Throttle
• Steering angle sensor  potentiometer
• Accelerometer (to realize an active seat-inclination-system (roll axis) at a later point)
• Individual wheel speed

1. Thanks to the potentiometer output, the individual wheel speed is calculated (steering kinematics)
2. To realize an efficient acceleration, I would like to realize a traction control aswell
After I have ready many threads (this one included), it seems the best way, to control the vescs with CAN
If you have some doubts about my assumptions please let me know  my background is 100% mechanical engineering :D

Is there a official dbc file for the vesc(6?) which would help to set up my can bus?

Thanks in advance

SG
Gabriel
 
I am also trying to implement VESC control over CAN. Has anyone else had any luck finding (or making?) a .dbc file for encoding/decoding CAN messages?
 
Back
Top