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.