CAN BUS interface - SimpleFOC standard identifiers?

I think there are a few makers on the forum that are doing stuff with CAN e.g. @erwin74. So I’m wondering if there is some sensible way we could collaborate.

A couple of ideas.

  1. work towards writing a SimpleCAN library that hides the mcu implementation. This is quite tough as the implementations vary so much and I’m worried that the ‘common denominator’ would be not great.
  2. work towards defining a set of SimpleFOC CAN frame definitions. This would allow common tooling to request telemetry and/or set config

So what do people think? I think I’m more interested in 2). So the frame definition might be something like:

b00000000000 identifier - is a 4 byte frame that sets (or reads) velocity P term for for motor#1
b00000000010 identifier - is a 4 byte frame that sets (or reads) velocity I term for motor#1  
b00010000000 identifier - is a 4 byte frame that sets (or reads) velocity P term for motor#2
b00010000010 identifier - is a 4 byte frame that sets (or reads) velocity I term motor#2  

So for 11 bit identifier maybe we would use 5 bits for identifying which motor and 6 bits for identifying which telemetry/config value.

I suspect this topic is only interesting to builders wanting to manage lots of motors. It is possible that we could extend @JorgeMaker’s SimpleFOC GUI tool to support CAN as well as Serial. You’d need a CAN/USB dongle and an OS that supports that dongle.

Anyone it’s an idea. I don’t have much CAN experience so am kind of looking to see if there is interest and also to see if we have anyone with more CAN experience who can perhaps advise. I’ve read a little about CANOpen - perhaps that might come in to play.


Hi @Owen_Williams

One month ago I bought a Canable clone on Aliexpress which never worked for me, 15€ wasted. … so I decided to develop my own Can Bus Sniffer.

It basically consists of an application that can be run on any STM32F4XX-based board + a transceiver. I guess with some minor modifications it can also be run on a BluePill but without using serial USB as USB and CAn share resources.

Captura de pantalla 2021-02-26 a las 1.20.59

Together with Snnifer I developed an application that allows you to see all the packets that circulate on the network and inject traffic. You can also send and receive traffic using a Python script. If you see that it may be interesting I will post it on my GitHub.

Sending/receiving CAN traffic with Python is quite simple :

from src.gui.caninspector.canDatagram import CANDatagram
from src.gui.caninspector.canSnifferDevice import CanSnifferDevice
import time
if __name__ == '__main__':
    portName = "/dev/tty.usbserial-14140"

    datagram1 = CANDatagram()
    datagram1.dlc = 2
    datagram1.messageID = 1111
    datagram1.rtr = False
    datagram1.extended = False = b'\01\02'

    snniffer =CanSnifferDevice(portName)

    while True:

As for defining an application layer on the Can Bus, instead of reinventing the wheel I would try to reuse as much as possible the mechanisms that already exist, such as CanOpen or Can TP, even if it is not complete. We can also take a look for inspiration at the protocol over CAN Bus developed by Gyems since what we what accomplish is quiet similar


1 Like

I’m a big fan of standardisation :slight_smile:

In the Drone-space there is a standard: UAVCAN -
I didn’t dig into the details, but perhaps there are things you can use?

There are also others who have considered the same problem, and built solutions:

In my opinion the more compatible all this is, the more benefit everyone has…

1 Like

That last link looks particularly useful. Odrive are trying to be canopen compatible. Adopting odrive ids might be a good place to start

1 Like

I’ve been using the ESP32CAN library for my robotics projects:

I started with this tutorial a couple years back, and then built up a custom USB2CAN adapter around it. Uber simple…

I use 8 bytes… [start, node, data0, data1, data2, data3, data4, stop]

I’m running @ 1000KBPS

1 Like

Not sure if this is the best place, since there are now a few threads about CAN-bus, but I wanted to ask you @Owen_Williams about the CAN-bus strategy on the driva-x2, and @Jason_Kirsons about your thinking for the CAN bus on your DRV8316 board…

I’m looking into adding CAN to my latest design, and would like to make it compatible with existing designs. Perhaps we can establish a standard way to do it, making our different modules interoperable :slight_smile:

I see the termination isn’t optional at the moment in your design Owen, and the VCC pin is powered.

Also the CAN isn’t isolated in either design.
I’d also go for unisolated at the moment. I don’t really have experience how bad a decision that is in practice, but the way I read it, it would depend on your use-cases. I would be connecting different nodes within the same robot - all powered by the same source and sharing close a common ground anyway - so not much need for isolation I would have said. It would be different if you were connecting boards located in different rooms, attached to possibly different ground potentials. But isolating the power will significantly increase the cost and maybe also PCB-space required.

I’m making the termination optional (via solder jumpers for the moment). That way the node can be placed anywhere in the CAN bus, not just the ends. I see you took that approach also, Jason.

I’m wondering why the SN65HVD230? It doesn’t do FDCAN? Is it because you want to power it from 3.3V only?

The Vout on the CAN port doesn’t seem very useful in the context of motor drivers, but I guess it could power a sensor board or something like that? I’m making it optional via solder-jumper, so you don’t have two nodes trying to power the +5V line. To be useful, I guess the node that powers it should provide up to 500mA?

Did you have a use in mind for it, Owen? I notice Jason you left it out, making for smaller ports. I would include a GND wire though, just like you both did, which nodes can use to align their potentials, if they were powered by different batteries for example, or connected to a shielding.

Final question: what connector would you use/are you using? JST-2.0mm?

Yes - so I can just solder the jumper on the last node to terminate.

Yes, because of 3.3v, and ESP32 does not support FDCAN.

My thinking for this was for when connecting to a PC - which would not be sharing the same power supply.

I’m using JST ZH series (1.5mm), but I would change if there was an agreed format that is not too bulky. I think a common hardware and software solution would be great.

A kind of “SimpleFOCcan compatible”…

Optional terminating resistor is very sensible (blush). my stm32f4 also doesn’t do fdcan. The g4 and h7 can do fdcan.

I can’t think of a brilliant reason for vcc on can, it’s a bit like the vcc on ftdi usb to serial (i never use that).

The user guide for b-g431-esc1 does talk about a use case for powered can. It’s actually the other direction e.g. the board can receive power over can to get/set config e.g from a flight controller board.

I’ve not tested driva-x2 - i did manufacture it but it’s been say on my desk (double blush)

Since i have been doing some dev on CAN nodes recently, here is my thoughts on termination. Even though it is a waste of a 120ohm resistor, I think the obvious choice is to make it a break away resistor or desolder-resistor. This way all boards will have termination. The user will then have to remove termination for nodes in between… ?

Im all for CAN integration. Take a look at how Adafruit has implemented it for the SAME51 Can Feather. Maybe there is some inspiration to sniff. Mind the CAN Standby option. I have not yet done a deep dive into this, but I do see the point in passive nodes.

Im going to jump on the can wagon:) For those using the SN65HVD230 is there a particular version you can recommend?
My setup for now is esp32 to stm32 blackpill f411

I used this one and got good results … it was the cheapest and It had the highest availability … but I just saw that it is currently out of stock.

For next time, I think I will have to buy modules on Aliexpress and desolder the IC to reuse it.

@JorgeMaker things are going fast…there is also SN65HVD230DG4 which is the same no…only 50 left omg…getting less everyday

1 Like

Check out MCP2562FD

They do recommend a choke for higher through-put.

Do any of you have experience with common mode chokes. As far as i gather, one needs to be careful with emf/ringing. I have a keep out area below the choke, but only on top layer just below the choke. Because of space restrictions I need a copper (gnd) layer/shield between the choke and the CAN FD IC on the flip side?

The windings on the choke is raised a bit from the board though…

1 Like

8 Mbps …pretty fast

Just want to add, there is a TVS diode on the lines just before the choke. This should hopefully minimize current buildup in the choke, acting like a valve.

I’ve been having a look at a few of these options.

  • CANopen - seems fairly complex - maybe I need a bit more understanding of what components are required. I can’t see what the licensing model is. Documentation seems to require registration?
  • CAN TP - looks to be a transport layer protocol, not application layer?
  • Gyems - no license seems to be specified.
  • ODrive - It looks like they have just specified the commands that they require, and structured it in a way that it’s somewhat CANopen compatible if you stay within limits. Not specifically licensed, but it is documented in a repository marked as MIT.
  • UAVCAN - Could be a little complex, but there are libraries that show Arduino implementations - they could be used as a starting point. Looks to be licensed in a way that we could use it.

My thoughts:
Implementing something like UAVCAN or CANopen would be great for integration with other components, but they come at the cost increased implementation time and possibly increased communication overhead.

ODrive “CAN Simple” - looks like it would be fast to implement, and have nearly no increased communication overhead. But the commands are tailored to ODrive.

If we have people with the time to do a UAVCAN implementation - and keep it maintained, that would be great. But if not, probably following the ODrive approach, but tailoring it to the SimpleFOC Commander interface would be the most realistic…

1 Like

Ok, I’ve started on the “ODrive” approach of having a Bus node id followed by a command, but the commands are targeted at the commander interface for SimpleFOC.

I’m using 20 bits of the 29 bit frame identifier:

Here is my example application - this is ESP32 specific, but I could abstract the can functions, having init/send/receive functions.

It integrates like this:

CANDriver can;
Commander command = Commander(;
can.init(7, 34, command);  // TX and RX pins

loop() {

Here is my setup, I’m using a jetson nano, but a RPi would be fine too.
Serial is connected for flashing/debugging, but can be disconnected.

Then from Linux, I can send can frames:

sudo ip link set can0 up type can bitrate 1000000 fd off
cansend can0 00011A00#19049E3F

Which SimpleFOC commander picks up and executes. (This is the set angle command).

So far I only have one way communication to SimpleFOC - so no replies yet. And only passing a float value.

I checked out your google doc. That looks like a good start.

I gave up on CANOpen (it doesn’t seem that open to me!) e.g. CAN 402 looked interesting but required some sign up. CANOpen seems a sensible approach if you are putting the device on a bus with other CANOpen devices. That way the devices will share things like discovery, reboot etc commands. But if you don’t have any CANOpen devices it is quite tough to ‘get going’ and/or be certain that you are actually canopen compatible.

I did consider following their approach for SDO / PDOs. SDOs - service data objects are more for read/write config I think. They push the command/subcommand into the first 3 bytes of payload (rather than in the identifier as you have it). PDOs are more for telemetry/real time stats. I think you can pack a bunch of data into those 8 payload bytes. I was also trying for 11bit header rather than 29.

One thing that is equally, or perhaps more, important for SimpleFOC than defining common CAN Frames is to have a cross mcu CAN library. It just doesn’t exist. So you can define the CAN Frames (as you’ve done) but the implementation will be stm32 specific or esp32 specific, or samd…

I’ve put CAN on the back burner for a bit, I still use it but only expose exactly what I need for my project. I’ll be following your progress.

I think this is just great!

My next design includes CAN-bus, and I will certainly try to use your protocol!
I’m using STM32 on that board, you’re on ESP32… So then we’d have at least 2 MCU implementations…

Some thoughts:
I like everything except the global commands. Maybe they are too opinionated? I somehow don’t like that Bus-Level commands are mixed with motor level commands, and that there is a limitation of 6 motors. Could the 9 extra bits perhaps be used for a “Motor ID” field, ie. to identify the target motor on the board?

I also don’t like the 16 node ID limit - seems too low to me. I think a 1 motor per node configuration will probably be most common, and a quadruped or humanoid could easily have 4 motors per limb, with no nodes left then for the head, neck, eyes or railgun. :wink:

Maybe the command fields should be somehow combined/flattened a bit more? At the moment it’s a sort of tree-like structure, but actually we waste a lot of the available space because only the motor global commands actually use the commands/sub-commands…

What about 8-byte ints? (used for example for timestamps)
And what about a boolean type?

Available commands:

  • I would include “multi-motor” commands, in particular a “Stop All” and “Disable All”
  • I would add a “Name” parameter to the “store values in NVRAM” command - so you can store multiple parameter sets. And I would make it per motor, not just for the whole node (the semantics are a bit unclear, maybe you meant it that way anyway)
  • therefore I would also add a “load values from NVRAM” command, which takes the “Name” of the parameter set to load
  • I think CMD_DECIMAL can be dropped? because over CAN commander would send binary values?
  • I think it would be cool to eventually define a Data Frame format for the monitoring as well…

Thanks so much for working on this, this is so awesome!

That would be great. I’m not sure how the STM32 is setup for can, but the ESP32 has an inbuilt “SJA1000 compatible” CAN controller - but it’s API calls completely hide it. If you were to use an external can controller through SPI/I2C- then we could even have CAN controller specific implementations - that work across all MCUs…

This was my thinking for splitting the command into Global Command / Command / Subcommand. Each one corresponds to a level used in the commander interface:

But I agree with you - it’s tree structured, and thus not efficient.
I’ll work on flattening the commands, and splitting the motor identifier.

Do you think 6 motors is not enough. Are any MCUs capable of this? I thought you would have 1 or 2 (in a dual controller). The motor identifier is only unique to the Node ID. So each node on the bus could potentially have 6 motors.

How would you route the CAN bus? it needs a termination resistor on each end. If you have 1 bus, you would have to loop the bus down a limb and back, so that you only have 1 start and end point. (Unless the bus can be a star shape?) I thought you would have had more than 1 bus (this also increases bandwidth). Anyway, I will add more node IDs.

Oh yes - I forgot about “long long” :slight_smile: I’ll add it.

Boolean can be represented by unsigned char. The CAN data payload is minimum is 1 byte right? So they would both use the same bandwidth for transmission. And when doing the printf back to the commander format - bool prints as 0/1 - the same as the unsigned char.

This is a good point. Maybe one of the motor IDs can be “ALL” and then whatever the command is gets issued to all motors.

I think saving/loading might be one of the hardest parts - as there’s not just 1 data structure we can save/load. The other method would be issuing lots of commands. Also it is controller specific.