Notes on Bafang CAN Bus

Bafang M200 (M-series) mid-drive motor with CAN bus
Bafang M200 mid-drive motor (model MM G210.250.C) with 36V battery connector (yellow, from left), empty 6-pin electric break connector (purple), 8-pin rear light and wheel speed connector, and 8-pin front light and display connector.

I got my wife this Accolmile Antelope 1S electric bicycle with Bafang components from their latest M-series lineup of mid-drive motors and displays which communicate over a CAN bus. It’s a great city bike and the price includes fast shipping from the warehouse in Poland. Their support was also quick to send a replacement wheel for the original that got dented during transport (despite the thoughtful packaging).

CAN over NodeJS

The M500/M600 motors are particularly popular with MTB riders so the communication protocol has been discussed in this forum thread and documented in this GitHub repository. Importantly, the BESST software for managing the configuration and firmware updates is an Electron app with all logic as plain HTML and JS files (and bundled source maps for all minified code), including the CAN frame specification. I’ve started documenting my research in this GitHub repository.

Connecting to CAN Bus

The display connector at the wheel is the most convenient place to tap into the CAN bus. I purchased two HIGO 5-pin connectors B5-F and S5-F from ETShop in Germany for around €7 each and soldered the CAN high (green), CAN low (white) and ground (black) wires from each for a connection to (a copy of) the CANable Pro 1.0 CAN-to-USB adapter. Be sure to test the wiring with a multimeter because one of the wires carries the full battery voltage!

HIGO S5-F connector wires for Bafang CAN bus

Technically any CAN-to-USB adapter will work (as long as it supports 250kbit transfer rate) and the choice depends more on the software you want to use for interacting with the bus. The CANable adapter runs the slcan firmware which implements the Lawicel SLCAN protocol (a basic ASCII serial) which is supported by various libraries, including SocketCAN.

Bafang HIGO S5 and B5 connectors attached to CANable Pro
HIGO 5-pin B5-F and S5-F connectors attached to CANable Pro 1.0 CAN-to-USB adaptor.
CANbus traffic between Bafang M200 and DP C245 display.
Logging CANbus traffic between Bafang M200 (G210.250.C) motor and DP C245 display.

Update Controller Configuration

I was able to test the setup by increasing the assisted maximum speed to 60km/h from the original 25km/h by sending a CAN message composed of the following components:

  • 0x05 for the message source (0x01 — torque sensor, 0x02 — controller, 0x03 — display or HMI, 0x04 — battery, 0x05 — BESST),
  • 0x02 for the message target (controller),
  • 0x00 for a WRITE operation (0x01 for READ),

which are transformed into a frame ID prefix for the 0x3203 code and subcode for “speed limit, wheel diameter and circumference” registry through this transformer function:

buildHexStringCommand = (source, target, opt, anfn, nfn) => {
    const cmdPrefix = hexAllocToBinaryStr(source.toString(16), 5) + hexAllocToBinaryStr(target.toString(16), 5) + hexAllocToBinaryStr(opt.toString(16), 3);
    let cmdPrefixHex = parseInt(cmdPrefix, 2).toString(16);
    if (cmdPrefixHex.length % 2 !== 0) {
        cmdPrefixHex = '0' + cmdPrefixHex;
    }
    let cmdHexString = cmdPrefixHex + anfn + nfn;
    return hexReverse(cmdHexString);
};

which returns:

05 10 32 03

as the frame ID along with the payload:

70 17 C0 2B B6 08 

where (with little-endian byte order):

  • 0x1770 is 6000 or 60km/h × 100,
  • 0x2BC0 is 700 or 700c wheel diameter (29-inch), and
  • 0x08B6 is 2230 or 2230mm wheel circumference.

2 Comments

  1. Andrey says:

    Hello. Maybe you will be interested in my project https://github.com/andrey-pr/OpenBafangTool . Its desktop configuration (and diagnostic) software for bafangs with can, and its already usable

  2. Zeus says:

    Thank you for the description: I found it clearer than elsewhere. I’m quite familiar with CAN, so all I needed is a description what message to send, and what is the wiring.

    To help others who find this article, I’ll add a few stumbling points that are not mentioned:

    – I didn’t buy a special cable or connectors but just connected the wires one by one. (I had to 3D-print a small part to make it possible on the male part). The catch is, the controller won’t turn on without the display, nor will it stay on for long if the display is suddenly disconnected. So it’s not enough to just jam the two CAN wires into the controller end; all 5 wires need to be connected through, and two of them branched to the CAN adapter. There may be a more convenient location which would allow a terminal connection (perhaps instead of some optional sensor), but my bike didn’t have it easily accessible, or I didn’t find one.

    – Given that the whole point is to send just one message, there is no need to have any custom software: any decent CAN software has this function, plus usually a nice GUI.

    – It’s worth mentioning that we are working with the so-called _extended_ CAN IDs, which are 29 bit long (as opposed to the standard 11 bits), in addition to the speed of 250 kbit/s. All CAN units/software support that, but it often needs to be enabled.

    – For _this_ one message, it is enough to know that the ID is 05 10 32 03. But since you provide the contents description, it is crucial to note the length of each field:
    * If we consider the ID as 32 bit for convenience, the first (leftmost!) 3 bits are unused, bringing the ID to 29 bits.
    * Then 5 bits for the source
    * 5 more bits for the destination
    * 3 bits for the operation
    * That’s the higher 16 bits. The lower 16 bits are the code and subcode (32 03).

    All this is, of course, just a Bafang’s invention: the CAN standard does not prescribe interpretation of the ID or data.

    The content is exactly 6 bytes long (generally it can be variable 0 to 8 and has to be set explicitly).

    I couldn’t work out the units for the wheel diameter (even though it’s probably only used for display). In any case, if we only want to change the speed limit, we’d better keep the existing values for the lower 4 bytes. To get them, we could probably request this message by changing the operation to READ, but there is no need to: the controller periodically sends this data anyway. We just need to run our favourite CAN software and intercept all messages for a few seconds while power is on. Then look for a message with the same 3203 code in the lower part of ID (mine was 02F83203), and copy the data from there, just replacing the first two bytes with the desired speed (low-endian in 0.01 km/h).

    Works a treat: just a single message to send, no handshakes, encryption or other higher-level stuff.

Leave a Reply