VESC CAN Message Structure

Does anyone have a well detailed write-up on the VESC CAN message structure? I’ve tried reverse engineering the code but it’s not making a whole lot of sense compared to what’s broadcast. I’ve found out that vedder seems to be using extended message IDs, and laid a bit of J1939 thinking in that the node id is essentially the source address in the status broadcast message. The last 2 bytes appear to be the commanded duty cycle in tenths of percent. I’ve yet to attempt commanding or intercepting command messages yet but I’ll be continuing this and if I map out the whole message structure, I’ll share my findings cause CAN is a very robust communication system that more of us should be using for safety.

Bit of an update, I’ve got the status broadcast message mostly solved, mostly meaning I’m not sure what increments the current is getting sent in. btw I’m using an arduino with a can bus module using the MCP_CAN library to run these tests. Also keep in mind that if your CAN bus is connected to more than 2 vescs, you need to mind the terminating resistors, probably will need to desolder them from the other vescs.

The ID is an extended ID: 0x00000900

The 9 is the message type, in this case it’s the status broadcast, the zeros after the 9 are what node it’s being sent from, in this case node 0, kinda like a J1939 source address. With my experiments so far, this is pretty much how you format the messages for transmitting them. In the case of transmission, the node number to the vesc you want to command, and the message type to what parameter you want to command. The message types are as follows:

  • Duty Cycle Set: 0
  • Current Set: 1
  • Current Set Brake: 2
  • RPM Set: 3
  • Pos(ition?) Set: 4
  • 5-8 have something to do with rx buffers, haven’t messed with it yet.
  • and status which is 9 as I said earlier.

The status message payload is organized as follows:

  • bytes 1-4 are the current RPM as a whole number, from most significant byte to least significant, respectively.
  • bytes 5 and 6 are the current current, most to least, but I’m not sure what units yet.
  • bytes 7 and 6 are the current dutycycle in 10ths of a percent, from most to least.

I’m still looking into making coherent commands, for some reason commanding rpm or current at 1 causes it to max out, and has a tendency to overflow repeatedly, think it’s a datatype packing issue, might make a library when I figure it out.

5 Likes

Looking forward to more updates.

Aight, so I’ve been wrestling with the current (message 1, 4 bytes, 32 bit int cast of float multiplied by 1000) control over CAN, and I finally got it to work, it was datatype flopping around. Dutycycle (message 0, 4 bytes, 0-100) and RPM (message 3, didn’t analyze it thoroughly enough) commands do work, I’m gonna try seeing what the rx buffer commands do, as I want to get voltage readings.

The way I’m reporting this is indeed very disorganized, but I do have a spreadsheet that’s coming together nicely.

Update: Okay so this parts getting complicated, message types 0-4 are direct command messages as I’ve outlined, very similar in what you would find in automotive applications, 5-8 however get a bit weird, and I’ve not run into this communication protocol before but I knew it was doable. Basically, instead of the messages being tied to a specific parameter, its a couple messages running a bunch of parameters, in this case the whole configuration and live data can be retrieved from specific vescs in any given CAN network, so long as they’ve been set with unique node IDs. 8 (CAN_PACKET_PROCESS_SHORT_BUFFER) seems to tell the vesc in the network what you want and passes along a flag in the 3rd byte to commands_process_packet to figure it out. I think vedder did this cause he already made a message processor for the serial, so why not just adapt it for the CAN? There’s a boat load of commands to comb through manually, I’m probably gonna stop when I get the essentials relating to power consumption and tachometer.

I think I cracked it, slightly inept coding skills kept me from seeing something obvious. Anyway, you can pretty much get any data you want from message 8, or rather the response on message 5 depending what you ask, you can completely configure a vesc via CAN. Now, something to note is I started experiencing speed issues regarding the response on message 5. You see, when message 5 sends back more than 8 bytes, it’ll chop up the message into multiple message 5s, with the first byte of each signalling where in the byte order message 5 is broadcasting. The speed issue comes in when you don’t have a way of pulling all messages out of the MCP2515 fast enough, it’ll start overwriting messages if it goes over 2. If you’re writing serial after every single message received, it introduces significant delay to the point where you’ll miss those messages coming in one after another, and for the running data, it’s 8 messages that need to be read pretty much as soon as they come in, and the vesc is sending them end to end. Adding an interrupt routine that has additional rx buffers to empty the MCP2515 before reporting to serial works, but they might not come in in the right order, which is fine cause byte 1 acts as your index. Asking for the config status is a few orders of magnitude more data that is outside the scope of what I want to investigate, and will likely need an mcu with integrated CAN drivers to handle properly. I’ll clean up my spreadsheet and come up with something neat, in the meantime, here’s a sneak peak.[removed cause it was wrong] hope ya’ll enjoy reading so far, I’ve learned a good chunk. It’ll take a while for the clean spread sheet and a possible arduino library, if you don’t wanna wait, the source code’s been extremely helpful in figuring it out, probably difficult to read for newbies, but hey, strike while the iron’s hot. Notepad++ is your friend.

5 Likes

Aight, so I’m releasing the basic messages here, I’m not sure when I’ll get around to a library as CAN bus is something you want to be directly controlling. I think effort is better spent on a tutorial for CAN bus specifically for esk8. Plus I’ve got other things that are taking priority atm. There’s other messages that I’ve not tested yet, you can do all the app configuration through CAN, that’s how the BLDC tool does it with CAN forwarding. Happy hacking!image

4 Likes

Thanks a lot for your instructions!! I can control my VESC through CAN now.

It’s probably too late now, but the vesc is open source :slightly_smiling_face: you don’t have to go through the trouble of reverse engineer anything, however you did a pretty good job figuring things out.

Message IDs are made of two parts, the lower 8 bits are for the actual node ID, while the remaining bits are the command packet IDs.

The CAN commands can be found here bldc/datatypes.h at master · vedderb/bldc · GitHub

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, ...etc

You can also see how the message ID are composed from the different command functions: example

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_eid(controller_id |
			((uint32_t)CAN_PACKET_SET_DUTY << 8), buffer, send_index);
}

And how the message id is decoded to get the node ID and packet command ID

if (rxmsg.IDE == CAN_IDE_EXT) {
				uint8_t id = rxmsg.EID & 0xFF;
				CAN_PACKET_ID cmd = rxmsg.EID >> 8;

Everything you need is just there in the source code :grin: https://github.com/vedderb/bldc/blob/master/comm_can.c

2 Likes

Is each VESC have a node? What I am getting at is that I may want to control four (4) VESC independently? Building a vehicle type system.

I would need to send each VESC messages independently of each other. Is this possible?

Thanks, neech

So each VESC has an assignable node ID, by setting that you effectively change the “source address” of the message that the VESC will respond to, and which message it sends it’s own data on. I’ve forgotten to keep this updated, basically the core messages (0-4) are consistent, but I was originally testing this with a 4.7 VESC, and things have changed a bit with the 4.12 VESC (I may be behind again tho). There have been discussions about the order of enumerations changing on , bit of a pain when stuff shuffles like that. I also have a vehicle project, but I won’t start really working it until probably May or June which will include more recent findings on my end.

Yes theoretically thats possible, even tho CAN messages are “seen” by all nodes, you can choose who is going to respond to it by using IDs.

However I noticed something strange the way vesc firmware handles message IDs, it seems like part of the message ID is composed of the command ID itself, so the type of command you are sending on the bus dictates the priority of messages.

I am trying to get the IMU data through CAN but even when I look at the sources, I can’t find anything about how IMU data are sent. I imagine it is in the “Get Data Long Buff”, I can see there is other types of information with other messages ID 0x38 or 0x3F, but I don’t know how to decode the bytes.

Hi, thank you for sharing your results! I set up a Nano with the MCP2515 configured the external ID with the commad and the Node ID which I got from the VESC Tool. Now my question. How do you set up the VESC Tool to accept the CAN input from the MCP2515? Or is there now configuration necessary? Thank you

I got the CAN data from the VESC, but how does that get decoded to useful info? Do you need to send a message for it to respond with more data?

Okay, so there have been some updates since I last brushed up on this. Working as much I can at the moment with your data, 0x80000967 is the status broadcast from 0x67 as denoted by PGN 0x0009. The first 4 bytes of the payload, the rpm of the motor reported is 0. The next 2 bytes is the current, which I’m pretty sure is in 2’s compliment which comes out to -1 so pretty much 0, it’s low precision current which I think is 10’s of amps. Last 2 bytes should be dutycycle, now I’m trying to remember if it’s little endian or big endian, I think little, basically zero. If you want more than that, you’ll have to request a bunch of info on PGN 0x0008, then it sends a multipacket on PGN 0x0005. I have a new project that’s having me get into this again, so if I think a big structure update is needed, I’ll see what I can do.

Okay, the 0x000E, 0x001B, 0x000F messages are apparently other statuses, according to the source code, one is missing, which I’m not surprised about because the MCP2515 library has had speed issues with handling messages that aren’t processed fast enough. It’s also annoying that the CAN messages are enumerated like this because they’re not in order. For some completeness, 0x001B is status 5, bytes 1-4 are tachometer, bytes 5 and 6 are the voltage, if my math is right, it’s reading about 16.2 volts. last two are reserved and aren’t use for anything. Probably will update my spreadsheet with the useful info at some point.

@ZFreaky

Thanks a lot for that! Really helpful info, I am now trying to extract those individual values to be able to display them on an OLED. Do you have any idea how to do that? Cheers