BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Get all your technical information about electric bikes here.
Post Reply
MCC   1 µW

1 µW
Posts: 4
Joined: Jun 21 2018 3:39am

BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by MCC » Jun 21 2018 7:40am

A few weeks ago I have bought a 500W 48V (DPC-18 Display) BBS02B kit to make my daily commute (15km one direction) a little bit easier. I'm very happy with the kit, already cycled more than 700 km's in a few weeks.

The thing is, this set is not entirely legal in the Netherlands. So I wanted to have a discrete and easy way to limit the speed of the bicycle (to say max 27 km/h) (not that I am naive enough to think that this would make things legal.. I just liked the challenge ;-) ). I was triggered by the fact that serial protocol is used to program the BBS controller.

I have recently started to learn working with Arduino's, and I thought: what if I place an Arduino between the display and the controller that simply forwards all comunnication between the two and when needed, it can send commands to the controller to program another speed limit. To do so, I have build the following circuit:
schemav2.jpg
I have used a RobotDyn Mega 2560 PRO MINI (https://robotdyn.com/mega-2560-pro-mini ... -16au.html) with the ATmega2560 chip. The RobotDyn is small and the ATmega2560 has a total of four hardware serial ports. I used a Hall sensor as an "invisible" switch to switch between unlimited speed ('by displays command') and limited speed (27 km/h). I used a display extension cable, to make the connections between the display and controller. I power the arduino using a 5v usb output I have on the battery pack. The final project looks like this:
20180530_100851.jpg
20180530_100851.jpg (125.02 KiB) Viewed 4166 times
20180530_100902.jpg
20180530_100902.jpg (92.37 KiB) Viewed 4166 times
20180530_101029.jpg
20180530_101029.jpg (146.7 KiB) Viewed 4166 times
I have glued a metal ring inside the box where the hall sensor is located. This metal ring holds the magnet in place on the outside of the box:
20180530_101007.jpg
20180530_101007.jpg (97.65 KiB) Viewed 4166 times
I have added a bluetooth HC05 module to monitor the traffic between the display and the controller and it gives me a way to interact with the Arduino and controller.

So, how does it work? With the box between the controller and display, the BBS works just like normal, all communication is just being forwarded between the display and the controller. When I take away the magnet, the Arduino programs predefined settings (from the Pedal Assist tab on Penoff's tool) where speed limit is set to 27km/h. The arduino follows this sequence:
- If the display is sending any command,wait for it to finish
- program the pedal assist settings and check confirmation by the controller
- change to PAS level 0
- go back to previous PAS level

In order for the settings to be effecitve (with the system on and while cycling), the PAS level has to be changed. Thats what the last two steps are for (go to PAS 0 en restore previous PAS level).

To program the Pedal Assist settings, I send the follolwing (HEX) command to the controller:

Code: Select all

16 53 0B 03 FF FF 14 04 04 FF 14 08 00 3C D2 
16 53 is the command for programming the Pedal Assist settings
0B 03 FF FF 14 04 04 FF 14 08 00 3C are the settings
D2 is a checksum bit.

Out off lazyness, I have used Penoff's software, to generate the command for me. I installed Com0Com (http://com0com.sourceforge.net/) and setup a virtual Com-port pair. On one of the two paired com-ports, I connect with a terminal (such as Hterm), on the other port of the pair I connect Pennoff's tool. Penoff's software is trying to connect with the controller (sending

Code: Select all

11 51 04 B0 05
to receive the general controller information). Using hterm, I respond:

Code: Select all

51 10 48 5A 58 54 53 5A 5A 36 32 32 32 30 31 31 01 14 1B 
Pennoff's tool now believes it is connected the controller (the response code contains general info about the controller type such as manufacturer, model, hardware version, firmware version etc.)

If you then go to the Pedal Assist tab, adjust the settings to your wishes and press "write", the software sends the command to the controller. You can capture this in the hterm terminal:
Knipsel.JPG
This way, I generated the command to program unlimited speed, and limited speed. Of course, you can also learn how the command is structured and build the command yourself.

In the next post, the full Arduino code that I'm currently using and in the post after, I'll describe more about what I have found out about the communication protocol.

Sources I have used:
https://github.com/philippsandhaus/bafang-python
viewtopic.php?f=2&t=74692&p=1207355
https://gitlab.com/bafang
https://penoff.me/2016/01/13/e-bike-con ... -software/
http://com0com.sourceforge.net/
https://chromium.googlesource.com/apps/ ... ster/hterm

MCC   1 µW

1 µW
Posts: 4
Joined: Jun 21 2018 3:39am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by MCC » Jun 21 2018 7:40am

The Arduino code:

Code: Select all

/*
   BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol
   Refer to this topic on https://endless-sphere.com for more information:
   https://endless-sphere.com/forums/viewtopic.php?f=2&t=94850&p=1389269
*/

//Commands for BBS controller
const byte Hspeed[] = {0x16, 0x53, 0x0B, 0x03, 0xFF, 0xFF, 0x14, 0x04, 0x04, 0xFF, 0x14, 0x08, 0x00, 0x3C, 0xD2}; //program pedal settings speed:'by displays command'
const byte Lspeed[] = {0x16, 0x53, 0x0B, 0x03, 0xFF, 0x1B, 0x14, 0x04, 0x04, 0xFF, 0x14, 0x08, 0x00, 0x3C, 0xEE}; //program pedal settings speed:27kph
const byte readPed[] = {0x11, 0x53}; // read pedal settings
const byte PAS0[] = {0x16, 0x0B, 0x00, 0x21, 0x00}; //command that display sends when turn off button is pressed


//Hall sensor variables
const int hallPin = 2;      // hall effect sensor pin
int hallState = 0;          // 0=UnlimitedSpeed(with magnet) 1=LimitedSpeed(without magnet), assume UnlimitedSpeed
bool firstHallValue = true; // Ignore first hall sensor reading, otherwise the BBS is programmed on each start-up

//variables for parsing display/controller data
byte DData[2];              //array to hold incomming display data
byte CData[3];              //array to hold incomming controller data
byte controllerResponse[20];//array to hold responses from controller

const byte DSpeed[] = {0x11, 0x20};               //Command that display sends to controller to receive speed data
const byte DAmps[] = {0x11, 0x0A};                //Command that display sends to controller to receive amp data
const byte Dmoving[] = {0x11, 0x31};              //Command that display sends to controller to receive info weather bike is stationarry or not
const byte DBatPerc[] = {0x11, 0x11};             //UNSURE: battery percentage?
const byte Dunknwn[] = {0x11, 0x08};              //UNKNOWN
const byte PASsuccessResp[] = {0x53, 0x0B, 0x5E}; // response from controller when pedal settings were successfully written

// First two bytes of setting PAS level
const byte DPAS[] = {0x16, 0x0B};
// Next two bytes PAS level
const byte DPAS0[] = {0x00, 0x21}; // PAS 0 16 0B 00 21
const byte DPAS1[] = {0x01, 0x22}; // PAS 1 16 0B 01 22
const byte DPAS2[] = {0x0B, 0x2C}; // PAS 2 16 0B 0B 2C
const byte DPAS3[] = {0x0C, 0x2D}; // PAS 3 16 0B 0C 2D
const byte DPAS4[] = {0x0D, 0x2E}; // PAS 4 16 0B 0D 2E
const byte DPAS5[] = {0x02, 0x23}; // PAS 5 16 0B 02 23
const byte DPAS6[] = {0x15, 0x36}; // PAS 6 16 0B 15 36
const byte DPAS7[] = {0x16, 0x37}; // PAS 7 16 0B 16 37
const byte DPAS8[] = {0x17, 0x38}; // PAS 8 16 0B 17 38
const byte DPAS9[] = {0x03, 0x24}; // PAS 9 16 0B 03 24

// Variables for parsing PAS level
bool PASExp = false;                          //True when PAS 'mask'(0x16, 0x0B) received
int PASbyte = 0;                              //counter that keeps track of how many PAS bytes were received (after 0x16, 0x0B)
int PASLevel = -1;                            //int holding PAS level
byte PASreInit[] = {0x16, 0x0B, 0x01, 0x22};  //command for re-initialising PAS level after speed limit change

//Variables for parsing Display commands and controller response
int DataExp = -1;                   //What data is expected from controller: -1=nothing 0=speed, 1=amps, 2=moving, 3=DBatPerc, 4=unknown
int nExpBytes = 0;                  //number of byte expected from controller
int byteNo = 0;                     //number of bytes received from controller
unsigned long  FirstByteTimestamp;  //time when first byte from controller was received
const float wheelsize_m = 2.2 ;     //wheelsize in meters

bool logging = false;   //enable/disable logging over HC05
int whosTalking = 0;    // keep track of who is talking; 0=display, 1=controller

void setup()
{
  Serial1.begin(115200);  //HC-05 module @TX-18 RX19
  Serial2.begin(1200);    //Display DP-C18 @TX14 RX15
  Serial3.begin(1200);    //BBS02 controller @TX16 RX17

  pinMode(hallPin, INPUT); // The hall effect sensor pin as an input
}

void loop()
{
  checkhall();
  readDisplay();
  readController();
  readTerminal();
}

void checkhall() {
  // reading the state of the hall effect sensor pin
  int hallStatetmp = digitalRead(hallPin);

  // Ignore first hall sensor reading, do not program BBS02
  if (firstHallValue) {
    hallState = hallStatetmp;
    firstHallValue = false;
    return; // do nothing
  }

  //Only program controller when hall sensor state changes (not every reading)
  if (hallState != hallStatetmp) {
    hallState = hallStatetmp;

    switch (hallState) {
      case 1: //no magnet
        changeSpeedLimitation(false); //Max 27kph
        break;

      case 0: //magnet present
        changeSpeedLimitation(true); //Unlimited (By displays command)
        break;
    }
  }
}

void changeSpeedLimitation(bool HighSpeed) {
  //to avoid programming controller in de middle of a display command
  //wait until controller starts responding
  while (whosTalking == 0) {
    readDisplay();
    readController();
  }

  //clear messages send by controller
  readControllerResponse(false);


  bool PASsucces = false;

  //program pedal settings, stop while loop when controller confirms success
  while (!PASsucces) {
    switch (HighSpeed) {
      case true: //
        programController(Hspeed, sizeof(Hspeed), true);
        break;
      case false: //
        programController(Lspeed, sizeof(Lspeed), true);
        break;
    }
    //check if controller gave expected response
    if (memcmp ( PASsuccessResp, controllerResponse, 3 ) == 0) {
      PASsucces = true;
    }
  }

  //Re-init current PAS-level
  programController(PAS0, sizeof(PAS0), false);
  programController(PASreInit, sizeof(PASreInit), false);

  //clear messages send by display in the mean time
  flushDisplay();
}

void programController(byte *command, int commandsize, bool echoResponse) {
  //write command to controller
  Serial3.write(command, commandsize);

  //Echo to HC05 (for debugging
  Serial1.print("writing: ");
  for (int i = 0; i < commandsize; i++)
  {
    Serial1.print(command[i], HEX);
  }
  Serial1.println("");

  //read controller response
  readControllerResponse(echoResponse);
}

void readControllerResponse(bool echo) {

  // To capture the variable length response from controller, wait max 'maxWaittime' for a next byte to be received
  // When maxWaittime is exceeded, asume controller finished sending response

  int maxWaittime = 200; //If nothing is received after  200 miliseconds, controller finished sending response
  bool exceeded_maxWaittime = false;
  unsigned long waitUntill;
  int byteNo = 0;

  waitUntill = millis() + maxWaittime;

  //monitor messages from controller until maxwaittime is exceeded
  while (!exceeded_maxWaittime)  {
    if (Serial3.available() > 0) {
      byte cByte = Serial3.read();

      //place received byte in array
      controllerResponse[byteNo] = cByte;

      if (byteNo < 18) {
        byteNo = byteNo + 1;
      }

      waitUntill = millis() + maxWaittime; //Adjust waitUntill
    }

    if (millis() > waitUntill) {
      exceeded_maxWaittime = true;
    }
  }

  //echo to HC05
  if (echo) {
    for (int i = 0; i < byteNo; i++) {
      Serial1.print(controllerResponse[i], HEX);
    }
    Serial1.println("");
  }
}


void flushDisplay() {
  //Read data from Display and do nothing, just clear the buffer
  while (Serial2.available() > 0) {
    byte dummy = Serial2.read();
  }
}

void readController() {
  // Read byte from the controller and send to Display
  if (Serial3.available())
  {
    parseController(Serial3.peek(), millis());
    Serial2.write(Serial3.read());
    whosTalking = 1;
  }
}

void readDisplay()  {

  // Read byte from the Display and send to controller
  if (Serial2.available())
  {
    parseDisplay(Serial2.peek());
    Serial3.write(Serial2.read());
    whosTalking = 0;
  }
}


void parseDisplay(byte Dincomming) {

  //shift bytes
  DData[0] = DData[1];
  DData[1] = Dincomming;

  //check if first two bytes of setting PAS level are received, next two bytes will determine PAS level
  if (memcmp ( DData, DPAS, sizeof(DData) ) == 0) {
    DataExp = -1;
    PASbyte = -1;
    PASExp = true; // next two bytes for setting PAS level are expected
  }


  //Keep track of how many PAS-level bytes are received
  if (PASExp) {
    PASbyte = PASbyte + 1;
  }

  //When two PASlevel bytes are received (after the 0x16, 0x0B mask), parse two PAS-level bytes
  if (PASbyte == 2) {
    PASExp = false; //no longer expecting PAS-level
    PASbyte = -1;//reset
    parsePAS();
  }

  //when logging turned off, skip rest of parsing
  if (!logging) {
    return;
  }


  //compare byte array 'DData' with known commands from display
  if (memcmp ( DData, DSpeed, sizeof(DData) ) == 0) {
    DataExp = 0;
    nExpBytes = 3;
  }
  else if (memcmp ( DData, DAmps, sizeof(DData) ) == 0) {
    DataExp = 1;
    nExpBytes = 2;
  }
  else if (memcmp ( DData, Dmoving, sizeof(DData) ) == 0) {
    DataExp = 2;
    nExpBytes = 2;
  }
  else if (memcmp ( DData, DBatPerc, sizeof(DData) ) == 0) {
    DataExp = 3;
    nExpBytes = 2;
  }
  else if (memcmp ( DData, Dunknwn, sizeof(DData) ) == 0) {
    DataExp = 4;
    nExpBytes = 1;
  }
  else {
    DataExp = -1;
    nExpBytes = 0;
  }

}

void parsePAS() {

  int oldPAS = PASLevel;

  //compare byte array with PAS levels
  if (memcmp ( DData, DPAS0, sizeof(DData) ) == 0) {
    PASLevel = 0;
  }
  else if (memcmp ( DData, DPAS1, sizeof(DData) ) == 0) {
    PASLevel = 1;
  }
  else if (memcmp ( DData, DPAS2, sizeof(DData) ) == 0) {
    PASLevel = 2;
  }
  else if (memcmp ( DData, DPAS3, sizeof(DData) ) == 0) {
    PASLevel = 3;
  }
  else if (memcmp ( DData, DPAS4, sizeof(DData) ) == 0) {
    PASLevel = 4;
  }
  else if (memcmp ( DData, DPAS5, sizeof(DData) ) == 0) {
    PASLevel = 5;
  }
  else if (memcmp ( DData, DPAS6, sizeof(DData) ) == 0) {
    PASLevel = 6;
  }
  else if (memcmp ( DData, DPAS7, sizeof(DData) ) == 0) {
    PASLevel = 7;
  }
  else if (memcmp ( DData, DPAS8, sizeof(DData) ) == 0) {
    PASLevel = 8;
  }
  else if (memcmp ( DData, DPAS9, sizeof(DData) ) == 0) {
    PASLevel = 9;
  }


  // Only do if PAS level actually changed
  if (PASLevel != oldPAS) {
    // set command for re-initialising current PAS level (in case speed limit needs to be changed)
    PASreInit[2] = DData[0];
    PASreInit[3] = DData[1];

    //Echo to HC05
    Serial1.print("*P");
    Serial1.print(PASLevel);
    //Serial1.print(",");
    //Serial1.print(millis());
    Serial1.print("*");
  }
}


void parseController(byte Cincomming, unsigned long timestamp) {
  //when logging turned off, skip procedure
  if (!logging) {
    return;
  }

  //skip procedure if no data is expected
  if (DataExp == -1) {
    byteNo = 0;
    return;
  }

  //timestamp when first byte was received
  if (byteNo == 0) {
    FirstByteTimestamp = timestamp;
  }

  //if bytes are expected, place byte in array and increment byteNo
  if (byteNo < nExpBytes) {
    CData[byteNo] = Cincomming;
    byteNo = byteNo + 1;
  }

  //when the number of expected bytes for a specific display command have been received, parse data
  if (nExpBytes == byteNo) {

    switch (DataExp) {
      case 0: //speed/
        Serial1.print("*S");
        Serial1.print((CData[1] + CData[0] * 256) * wheelsize_m * 60 / 1000); //CData[1]=rpm when rpm>255 CData[0]=1 then 256 should be added to CData[1] for correct rpm
        //Serial1.print(",");
        //Serial1.print(FirstByteTimestamp);
        Serial1.print("*");
        break;
      case 1: //amps
        Serial1.print("*A");
        Serial1.print(CData[0] / 2); // divided by two this seems to be the Amps value
        //Serial1.print(",");
        //Serial1.print(FirstByteTimestamp);
        Serial1.print("*");
        break;
      case 2: //moving
        Serial1.print("*M");
        Serial1.print(CData[0]); //30=stationary, 31=bike is moving
        //Serial1.print(",");
        //Serial1.print(FirstByteTimestamp);
        Serial1.print("*");
        break;
      case 3: //BatteryPercentage (?)
        Serial1.print("*B");
        Serial1.print(CData[0]);
        //  Serial1.print(",");
        // Serial1.print(FirstByteTimestamp);
        Serial1.print("*");
        break;
      case 4: //UNKNOWN
        Serial1.print("*U");
        Serial1.print(CData[0]);
        // Serial1.print(",");
        //Serial1.print(FirstByteTimestamp);
        Serial1.print("*");
        break;
    }
    byteNo = 0;//reset
    DataExp = -1;//reset
  }
}

void readTerminal()  {
  // read commands received from terminal
  if (Serial1.available())
  {
    char terminal =  Serial1.read();

    switch (terminal) {
      case 'l'://switch to limited speed
        changeSpeedLimitation(false);
        break;
      case 'h'://switch to unlimited speed
        changeSpeedLimitation(true);
        break;
      case '0':     // turn logging off
        logging = false;
        break;
      case '1':      // turn logging on
        logging = true;
        break;
    }
  }
}






Last edited by MCC on Jun 21 2018 9:17am, edited 2 times in total.

MCC   1 µW

1 µW
Posts: 4
Joined: Jun 21 2018 3:39am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by MCC » Jun 21 2018 7:40am

In order to better understand the BBS communication protocol, I started with simply forwarding all traffic between Display and controller and monitor this traffic using the HC05 bluetooth module (monitoring this with a terminal client). This resulted in logs like:

Code: Select all

21145-Display says:
11 20  
21172-Controller says:
0 0 20  
21237-Display says:
11 11  
21263-Controller says:
41 41  
21328-Display says:
11 31  
21353-Controller says:
30 30  
21409-Display says:
11 22 33 11 24 35 16 1A F0 11 8  
21953-Controller says:
1 

In general the conversation between the display and controller is led by the display (display sends a message, controller responds). I have analysed the protocol, this is what I have found so far:

11 0A Amps
controller responds with two bytes, for example:

Code: Select all

Display: 11 0A
Controller: 04 04
Display: 11 0A
Controller: 0C 0C
Display: 11 0A
Controller: 0F 0F
Display: 11 0A
Controller: 19 19
The two bytes are always identical. This byte, converted to decimal and divided by two, holds the Amp value. This value is the amount of amps the controller determined the motor should have, so it is not a measured value.

11 20 Wheel RPM
controller responds with three bytes, for example:

Code: Select all

Display: 11 20
Controller: 0 53 73
Display: 11 20
Controller: 0 5C 7C
Display: 11 20
Controller: 0 63 83
Display: 11 20
Controller: 0 6E 8E
Display: 11 20
Controller: 0 7D 9D
The second byte (converted to decimal) holds the wheel RPM measured. The third byte seems to contain the same signal as the second byte, but with an offset of +32. The first byte is 0 when wheel RPM <= 256 and 1 when wheel RPM > 256. When wheel RPM > 256, the second byte starts from 0 again.

From these values, the speed (in km/h) of the bike can be calculated:

Code: Select all

rpm * wheelsize_in_meters * 60 / 1000
([byte 2] + [byte 1] * 256) * wheelsize_in_meters * 60 / 1000)
The interval at which this message is sent is irregular (ranging from .8 to a couple of seconds). So if you want to use speed to determine distance traveled, you will need to know the time between the two samples.

11 31 Bycycle moving/stationary
controller responds with two bytes, for example:

Code: Select all

Display: 11 31
Controller: 30 30
Display: 11 31
Controller: 31 31
The two response bytes are always identical and seem the indicate weather speed=0 (stationary) or speed >0 (moving). Value 30 indicates stationary and value 31 indicates moving. I assume that the display uses this information to calculate average speed while moving and to shut down after a certain time of inactivity.

16 1A F1 lights on
typical response from controller: 1
message is sent by the display periodically and when user turns lights on

16 1A F0 lights off
typical response from controller: 1
message is sent by the display periodically and when user turns lights off

11 08 UNKNOWN pedal activity?
controller responds with one byte, for example:

Code: Select all

Display: 11 08
Controller: 1
Display: 11 08
Controller: 0
Controller response is 1 or 0. Not sure what this signal relates to, might be pedal activity:
11 08.JPG
11 08.JPG (102.13 KiB) Viewed 4165 times

11 11 Battery Percentage?
controller responds with two bytes, for example:

Code: Select all

Display: 11 11
Controller: 64 64
Display: 11 11
Controller: 5E 5E
Display: 11 11
Controller: 5F 5F
The two response bytes are always identical and seem to hold 'battery percentage'. Converted to decimal, this value is never higher than 100 and decreases during a trip. This value also drops when a lot of amps are drawn by the motor and rises again when the motor is doing nothing (voltage sag). Because of this voltage sag, it generates quite a noisy signal:
battery.JPG
battery.JPG (94.21 KiB) Viewed 4165 times
PAS levels 16 0B xx xx

PAS levels are sent by the display periodically (and when user changes PAS level of course). Controller does not send a response.

Code: Select all

16 B 0 21  	PAS 0
16 B 1 22  	PAS 1
16 B B 2C  	PAS 2 (or PAS 1 for 5 PAS levels)
16 B C 2D  	PAS 3 (or PAS 1 for 3 PAS levels)
16 B D 2E  	PAS 4 (or PAS 2 for 5 PAS levels)
16 B 2 23  	PAS 5 (or PAS 2 for 3 PAS levels)
16 B 15 36  	PAS 6 (or PAS 3 for 5 PAS levels)
16 B 16 37  	PAS 7
16 B 17 38  	PAS 8 (or PAS 4 for 5 PAS levels)
16 B 3 24  	PAS 9 (or PAS 5 for 5 PAS levels) (or PAS 3 for 3 PAS levels)
UNKNOWN messages
11 21 32 unknown
no response from controller, often you see this message combined with other messages, e.g.
Display:11 22 33 11 24 35 11 21 32 11 08
Controller responds to the "11 08" message from the display

11 22 33 unknown
no response from controller, often you see this message combined with other messages, e.g.
Display:11 22 33 11 24 35 11 21 32 11 08
Controller responds to the "11 08" message from the display

11 24 35 unknown
no response from controller, often you see this message combined with other messages, e.g.
Display:11 22 33 11 24 35 11 21 32 11 08
Controller responds to the "11 08" message from the display

11 25 36 unknown
no response from controller, often you see this message combined with other messages, e.g.
Display: 11 25 36 11 08
Controller responds to the "11 08" message from the display


Typical start-up converstion:
Display says:
11 11
Controller says:
0
Display says:
11
Controller says:
0
Display says:
11
Controller says:
0 0
Display says:
11 90 11
Controller says:
90
Display says:
90
Controller says:
40 D0 90 40 D0

Not sure what information is exchanged there. I can imagine that it contains speed limit that is set in the display (the controller should have this info to limit the speed when set to 'Speed limit By display's command')

16 1F 02 9F D6 unknown
16 1F 2 55 8C unknown

I have set-up the Arduino to recognise 'known' messages and responses from the display/controller parse them and sent this information in a predefined format over bluetooth using the HC05 module. You can connect your phone to HC05 module and using the app Bluetooth Electronics (https://play.google.com/store/apps/deta ... ooth&hl=nl) you can visualise the current signals on your phone:
Screenshot_20180604-164453.png

User avatar
footloose   10 kW

10 kW
Posts: 584
Joined: Mar 29 2013 2:59pm
Location: San Mateo, CA

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by footloose » Jun 21 2018 8:43am

Thumbs up! Subscribed.

Tommm   10 kW

10 kW
Posts: 594
Joined: Apr 03 2018 2:32am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by Tommm » Jun 23 2018 8:31am

Simple and ugly version=switch for throttle bypass and flick up the pas setting to 9 that you configured to be 50% power and 50% speed so you won't reach the legal limit even in different gearing (not enough power).

User avatar
fechter   100 GW

100 GW
Posts: 14178
Joined: Dec 31 2006 3:23pm
Location: California Bay Area, USA

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by fechter » Jul 23 2018 8:02am

Wow, I missed this earlier. This is great information.

If enough of the protocol gets deciphered, it would be possible to make your own display unit with custom features, including the magnet switch.

Another nice feature would be to have an accurate battery meter for 52v packs.
"One test is worth a thousand opinions"

User avatar
Ivanovitch_k   10 W

10 W
Posts: 96
Joined: Jul 15 2017 4:54pm
Location: Paris, France

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by Ivanovitch_k » Jul 23 2018 5:53pm

fechter wrote:
Jul 23 2018 8:02am
If enough of the protocol gets deciphered, it would be possible to make your own display unit with custom features, including the magnet switch.
this is well underway at the french forum. ESP32 implementation.
BBS'ed Radon ZR Team 8:
BBS02B 750W - DPC750C - 48V 17.5Ah GA shark - 11 spd 11-40 Shimano XT, 42T N-W Precialps CW, KMC X11e chain, Gearsensor, XT RD, XTR Shifter, F Saint Brake, R XT Brake, RT99 200/160 Discs, magnetic shutoff, DT Swiss X1900 QR9 wheels, Manitou M30 air fork, 2.4" slick balloon schwalbe Super Moto X tires @2 bars, Suntour NCX suspension seatpost, ergon SFC3 saddle, ergon GP3 grips, Supernova M99 Pro + E12 Taillight


DaDo.Bzz   100 W

100 W
Posts: 130
Joined: Feb 12 2015 3:29pm

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by DaDo.Bzz » Aug 29 2018 6:34am

MCC nice job! You must have spent hundreads of hours with this project.

So that chinese bafang protocol does seem quite dumb. But as it is simple it can be reversed engineered.

I have been developin a LCD display with actuall pas, gps and range back 3y ago when I bought my first bafang. If I would know it us so simple protocol, would calculate it just from voltage, current and speed info.

Now there us egg rider, nice lcd, but does not have remote buttons. I would like to have lcd in the center of handlebars.

Thanks for that bt software, did not know about it! Is it BT SPP protocol? But again just Android.

Now I am playing with Tiny BMS and modbus.
LMX 81, 16S6P 60A
BBS02 on Cannondale Rz 120
BBSHD on CTM Scroll, XTR Di2, 700Wh 52V
Slovakia, Bratislava

7200rpm   10 µW

10 µW
Posts: 5
Joined: Jun 13 2016 6:48pm

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by 7200rpm » Sep 10 2018 10:16pm

Well done MCC! Great work!

I have been thinking of doing something similar, but was originally planning to ditch the display altogether and just terminate the display cable directly into a bluetooth serial module and control through an android app (for analytics). But now I see that you can add the module in between the controller and display. Few questions:

1) How is it working out? Does the module "drop" any commands it is supposed to forward to display?

2) Have you done any further development? I would be interested in writing an Android app - would you care to share the interface from bluetooth to Android (or is it just BBS serial?)

Cheers!

joq3   10 mW

10 mW
Posts: 20
Joined: Sep 05 2018 1:48am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by joq3 » Jan 22 2019 9:07am

Wow, this is exactly what I am looking for. Would this work on the Bafang Ultra 1000W? Or is it different somehow?

User avatar
neptronix   100 GW

100 GW
Posts: 13971
Joined: Jun 15 2010 5:56pm
Location: California refugee living in Utah, USA
Contact:

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by neptronix » Jan 22 2019 10:01am

Brilliant! Thank you for sharing this with us!
Efficiency is everything :bolt:

My first major build: 1.6kW 8T MAC motor on a Trek 4500.
The new all-arounder: Leafmotor 1500w @ 4kW on a Turner O2 full suspension.
The monster scooter: 20" eZee on a Cannondale Semi Recumbent.
Whipper-snapper: ? on a lightweight BikeE Semi Recumbent

joq3   10 mW

10 mW
Posts: 20
Joined: Sep 05 2018 1:48am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by joq3 » Feb 20 2019 12:26pm

Would it be possible to modify the firmware in the DPC-18 to be able to do this by holding one of the buttons?

If not, do you think this will work without modification on a Bafang Ultra? The display is the same I think. But the engine is different.

EDIT: Just got com0com and HTerm setup, and tried using this with the newer Bafang programming software (found here https://electricbike-blog.com/2017/11/2 ... ike-drive/ )
And I cannot get it to connect, I get this error: Read The Controller Info CheckSum Error.
But there is no more information. I tried removing the ending 1B which is for checksum, but it doesn't work.
How do I find out what the correct checksum is?

joq3   10 mW

10 mW
Posts: 20
Joined: Sep 05 2018 1:48am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by joq3 » Feb 21 2019 1:53pm

I actually got some progress done. I managed to listen to the COM-port with a Serial Port Monitor application when I connected the bike with the Bafang Programming software. So I now have the correct response to be able to connect with com0com virtual ports.
The connection request from the programming software looks like this:

Code: Select all

11 51 25 80 F6
This is the response sent from my bike:

Code: Select all

51 10 53 5A 42 46 53 57 31 32 31 35 31 30 30 34 02 1E EE
Tried it with com0com and HTerm and I am able to connect and I have managed to capture the 25km/h settings too.

I have also confirmed that the Read pedal assist settings looks like this:

Code: Select all

11 53
Which means I got a good chunk done already for the Bafang Ultra.
What I cannot figure out is how to get this command "Command that display sends when turn off button is pressed"?
And all the PAS commands?
Plus the DSpeed, DAmps, Dmoving, DBatPerc, Dunknwn, PASsuccessResp?
Do I need to figure out all these too? Or is it possible to strip the code of some of the functions. I don't really need the part that shows the graphs and percentage etc. only the cop mode button.

guancio   10 mW

10 mW
Posts: 29
Joined: Jun 04 2019 8:10am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by guancio » Jun 14 2019 9:49am

Really nice work. I hope to get my kit and play around. Maybe we can create a bafang specific cycle analyst.

Tomblarom   1 µW

1 µW
Posts: 4
Joined: Jul 20 2018 1:33pm

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by Tomblarom » Sep 04 2019 7:44am

Hey! Has anybody brought it to work? I'm stuck and initially planned to use it for my brakelight system. I'm getting "Error 30: Communication fault". Havn't changed the arduino code, beside skipping HC-05 and using "Micro-USB COM Serial", while using Arduinos Serial Monitor. Using an BBSHD, custom profile. My wiring looks like this:

Code: Select all

D: Display
C: Controller
Image
Last edited by Tomblarom on Oct 05 2019 5:25pm, edited 1 time in total.

guancio   10 mW

10 mW
Posts: 29
Joined: Jun 04 2019 8:10am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by guancio » Sep 07 2019 2:08pm

Tomblarom wrote:
Sep 04 2019 7:44am
Hey! Has anybody brought it to work? I'm stuck and initially planned to use it for my brakelight system. I'm getting "Error 30: Communication fault". Havn't changed the arduino code, beside skipping HC-05 and using "Micro-USB COM Serial", while using Arduinos Serial Monitor. Using an BBSHD, custom profile. My wiring looks like this:

Code: Select all

D: Display
C: Controller
Image
Have you tried to remove all logs from the serial port? During my experiments I had some trouble because I was introducing too much delay in the communication.

John in CR   100 GW

100 GW
Posts: 13824
Joined: May 20 2008 12:58am
Location: Paradise

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by John in CR » Sep 07 2019 7:12pm

"Cop Button"??? Where do you live that cops don't have anything better to do and are hassling ebikers, so we can all avoid it?

Tomblarom   1 µW

1 µW
Posts: 4
Joined: Jul 20 2018 1:33pm

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by Tomblarom » Oct 05 2019 5:24pm

I found the schematics quite confusing, since you can use colors and additional parts in Fritzing. Thanks to the help of @guancio, I finally was able to get the wiring working and made a new wiring diagram. In my case I used Serial1 and Serial2. Fritzing file in attachments.

I'm currently working on a solution to implement brakelight, control turn signals and read specific events between Controller / Display to control the lights.

While my testings I couldn't establish a connection to the controller without the resistors. People told, it's possible without any. I used this simple forward Arduino Sketch: https://forum.arduino.cc/index.php?topic=614872.0

Code: Select all

  Serial.begin(115200);   //PC Serial Com Port
  Serial1.begin(1200);    //BBS02 controller    @RX18 TX19
  Serial2.begin(1200);    //Display DP-C18      @TX16 RX17
Image

Edit: Swapped TX / RX of display
Attachments
cop_button_wiring_v2.zip
Swapped TX / RX of display
(215.03 KiB) Downloaded 5 times
Last edited by Tomblarom on Oct 06 2019 11:30am, edited 2 times in total.

User avatar
Roots Rocker   1 mW

1 mW
Posts: 14
Joined: Dec 29 2018 10:54am

Re: BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol

Post by Roots Rocker » Oct 06 2019 9:29am

Hello,
Swap 16 with 17...(Tx Rx 2)

mfg Martin

Post Reply