Advertisement
Guest User

Untitled

a guest
Mar 23rd, 2020
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.01 KB | None | 0 0
  1. /*
  2.   BAFANG BBSxx change settings on the fly (aka 'cop button') and BBSxx communication protocol
  3.   Refer to this topic on https://endless-sphere.com for more information:
  4.   https://endless-sphere.com/forums/viewtopic.php?f=2&t=94850&p=1389269
  5. */
  6.  
  7. //Commands for BBSxx controller
  8. const byte HspeedBasic[] = {0x16, 0x52, 0x18, 0x2A, 0x1E, 0x00, 0x0F, 0x14, 0x19, 0x1E, 0x28, 0x32, 0x3C, 0x50, 0x64, 0x00, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x34, 0x01, 0x0F}; //program basic settings speed:'by displays command'
  9. const byte HspeedPas[] = {0x16, 0x53, 0x0B, 0x03, 0xFF, 0xFF, 0x0A, 0x04, 0x02, 0xFF, 0x0A, 0x08, 0x00, 0x50, 0xD0};   //program pedal settings speed:'by displays command'
  10. const byte LspeedBasic[] = {0x16, 0x52, 0x18, 0x2A, 0x14, 0x00, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15, 0x18, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x01, 0x64}; //program basic settings speed:27kph
  11. const byte LspeedPas[] = {0x16, 0x53, 0x0B, 0x03, 0xFF, 0x19, 0x0A, 0x04, 0x02, 0xFF, 0x0A, 0x08, 0x00, 0x50, 0xEA};   //program pedal settings speed:27kph
  12. const byte readPed[] = {0x11, 0x53}; // read pedal settings
  13. const byte PAS0[] = {0x16, 0x0B, 0x00, 0x21, 0x00}; //command that display sends when turn off button is pressed
  14.  
  15.  
  16. //Hall sensor variables
  17. const int hallPin = 46;      // hall effect sensor pin
  18. int hallState = 0;          // 0=UnlimitedSpeed(with magnet) 1=LimitedSpeed(without magnet), assume UnlimitedSpeed
  19. bool firstHallValue = true; // Ignore first hall sensor reading, otherwise the BBSxx is programmed on each start-up
  20.  
  21. //variables for parsing display/controller data
  22. byte DData[2];              //array to hold incomming display data
  23. byte CData[3];              //array to hold incomming controller data
  24. byte controllerResponse[20];//array to hold responses from controller
  25.  
  26. const byte DSpeed[] = {0x11, 0x20};               //Command that display sends to controller to receive speed data
  27. const byte DAmps[] = {0x11, 0x0A};                //Command that display sends to controller to receive amp data
  28. const byte Dmoving[] = {0x11, 0x31};              //Command that display sends to controller to receive info weather bike is stationarry or not
  29. const byte DBatPerc[] = {0x11, 0x11};             //UNSURE: battery percentage?
  30. const byte DLight[] = {0x16, 0x1A};               //Command Light On/Off
  31. const byte DBrake[] = {0x11, 0x08};               //Command that display sends to controller to receive brake staus
  32. const byte BasicSuccessResp[] = {0x52, 0x18, 0x6A}; // response from controller when pedal settings were successfully written
  33. const byte PASsuccessResp[] = {0x53, 0x0B, 0x5E}; // response from controller when pedal settings were successfully written
  34.  
  35. // First two bytes of setting PAS level
  36. const byte DPAS[] = {0x16, 0x0B};
  37. // Next two bytes PAS level
  38. const byte DPAS0[] = {0x00, 0x21}; // PAS 0 16 0B 00 21
  39. const byte DPAS1[] = {0x01, 0x22}; // PAS 1 16 0B 01 22
  40. const byte DPAS2[] = {0x0B, 0x2C}; // PAS 2 16 0B 0B 2C
  41. const byte DPAS3[] = {0x0C, 0x2D}; // PAS 3 16 0B 0C 2D
  42. const byte DPAS4[] = {0x0D, 0x2E}; // PAS 4 16 0B 0D 2E
  43. const byte DPAS5[] = {0x02, 0x23}; // PAS 5 16 0B 02 23
  44. const byte DPAS6[] = {0x15, 0x36}; // PAS 6 16 0B 15 36
  45. const byte DPAS7[] = {0x16, 0x37}; // PAS 7 16 0B 16 37
  46. const byte DPAS8[] = {0x17, 0x38}; // PAS 8 16 0B 17 38
  47. const byte DPAS9[] = {0x03, 0x24}; // PAS 9 16 0B 03 24
  48.  
  49. // Variables for parsing PAS level
  50. bool PASExp = false;                          //True when PAS 'mask'(0x16, 0x0B) received
  51. int PASbyte = 0;                              //counter that keeps track of how many PAS bytes were received (after 0x16, 0x0B)
  52. int PASLevel = -1;                            //int holding PAS level
  53. byte PASreInit[] = {0x16, 0x0B, 0x01, 0x22};  //command for re-initialising PAS level after speed limit change
  54.  
  55. // Variable for parsing light
  56. bool LightExp = false;
  57.  
  58. // Variables for parsing Display commands and controller response
  59. int DataExp = -1;                   //What data is expected from controller: -1=nothing 0=speed, 1=amps, 2=moving, 3=DBatPerc, 4=unknown
  60. int nExpBytes = 0;                  //number of byte expected from controller
  61. int byteNo = 0;                     //number of bytes received from controller
  62. unsigned long  FirstByteTimestamp;  //time when first byte from controller was received
  63. const float wheelsize_m = 2.2 ;     //wheelsize in meters
  64.  
  65. bool logging = false;   // enable/disable logging over HC05
  66. int whosTalking = 0;    // keep track of who is talking; 0=display, 1=controller
  67. bool light = false;     // status of light
  68. bool brake = false;     // status if the brake is blocking
  69.  
  70.  
  71. void setup()
  72. {
  73.   Serial.begin(115200);   // PC Serial Com Port
  74.   Serial1.begin(1200);    // BBSHD   Controller  @RX18 TX19
  75.   Serial2.begin(1200);    // DP-C18  Display     @TX16 RX17
  76.  
  77.   pinMode(hallPin, INPUT); // The hall effect sensor pin as an input
  78. }
  79.  
  80. void loop()
  81. {
  82.   checkhall();
  83.   readDisplay();
  84.   readController();
  85.   //readTerminal();
  86. }
  87.  
  88. void checkhall() {
  89.   // reading the state of the hall effect sensor pin
  90.   int hallStatetmp = digitalRead(hallPin);
  91.  
  92.   // Ignore first hall sensor reading, do not program BBSxx
  93.   if (firstHallValue) {
  94.     hallState = hallStatetmp;
  95.     firstHallValue = false;
  96.     return; // do nothing
  97.   }
  98.  
  99.   //Only program controller when hall sensor state changes (not every reading)
  100.   if (hallState != hallStatetmp) {
  101.     hallState = hallStatetmp;
  102.  
  103.     switch (hallState) {
  104.       case 1: //no magnet
  105.         changeSpeedLimitation(false); //Max 27kph
  106.         break;
  107.  
  108.       case 0: //magnet present
  109.         changeSpeedLimitation(true); //Unlimited (By displays command)
  110.         break;
  111.     }
  112.   }
  113. }
  114.  
  115. void changeSpeedLimitation(bool HighSpeed) {
  116.   //to avoid programming controller in de middle of a display command
  117.   //wait until controller starts responding
  118.   while (whosTalking == 0) {
  119.     readDisplay();
  120.     readController();
  121.   }
  122.  
  123.   //clear messages send by controller
  124.   readControllerResponse(false);
  125.  
  126.   bool PASsucces = false;
  127.  
  128.   //program pedal settings, stop while loop when controller confirms success
  129.   while (!PASsucces) {
  130.     switch (HighSpeed) {
  131.       case true: //
  132.         programController(HspeedBasic, sizeof(HspeedBasic), true, "HspeedBasic");
  133.         programController(HspeedPas, sizeof(HspeedPas), true, "HspeedPas");
  134.         break;
  135.       case false: //
  136.         programController(LspeedBasic, sizeof(LspeedBasic), true, "LspeedBasic");
  137.         programController(LspeedPas, sizeof(LspeedPas), true, "LspeedPas");
  138.         break;
  139.     }
  140.     //check if controller gave expected response
  141.     if (memcmp ( PASsuccessResp, controllerResponse, 3 ) == 0 || memcmp ( BasicSuccessResp, controllerResponse, 3 ) == 0) {
  142.       PASsucces = true;
  143.     }
  144.   }
  145.  
  146.   //Re-init current PAS-level
  147.   programController(PAS0, sizeof(PAS0), false, "PAS0");
  148.   programController(PASreInit, sizeof(PASreInit), false, "PASreInit");
  149.  
  150.   //clear messages send by display in the mean time
  151.   flushDisplay();
  152. }
  153.  
  154. void programController(byte *command, int commandsize, bool echoResponse, String desc) {
  155.   //write command to controller
  156.   Serial1.write(command, commandsize);
  157.  
  158.   //Echo to USB (for debugging)
  159.   Serial.println();
  160.   Serial.print("writing  ");
  161.   Serial.print(desc);
  162.   Serial.print(": ");
  163.   for (int i = 0; i < commandsize; i++)
  164.   {
  165.     Serial.print(command[i], HEX);
  166.   }
  167.  
  168.   //read controller response
  169.   readControllerResponse(echoResponse);
  170. }
  171.  
  172. void readControllerResponse(bool echo) {
  173.  
  174.   // To capture the variable length response from controller, wait max 'maxWaittime' for a next byte to be received
  175.   // When maxWaittime is exceeded, asume controller finished sending response
  176.  
  177.   int maxWaittime = 300; //If nothing is received after  200 miliseconds, controller finished sending response
  178.   bool exceeded_maxWaittime = false;
  179.   unsigned long waitUntill;
  180.   int byteNo = 0;
  181.  
  182.   waitUntill = millis() + maxWaittime;
  183.  
  184.   //monitor messages from controller until maxwaittime is exceeded
  185.   while (!exceeded_maxWaittime)  {
  186.     if (Serial1.available() > 0) {
  187.       byte cByte = Serial1.read();
  188.  
  189.       //place received byte in array
  190.       controllerResponse[byteNo] = cByte;
  191.  
  192.       if (byteNo < 18) {
  193.         byteNo = byteNo + 1;
  194.       }
  195.       waitUntill = millis() + maxWaittime; //Adjust waitUntill
  196.     }
  197.  
  198.     if (millis() > waitUntill) {
  199.       exceeded_maxWaittime = true;
  200.     }
  201.   }
  202.  
  203.   //when logging turned off, skip procedure
  204.   if (!logging) {
  205.     return;
  206.   }
  207.  
  208.   //Echo to USB
  209.   if (echo) {
  210.     Serial.println("");
  211.     Serial.print("response: ");
  212.     for (int i = 0; i < byteNo; i++) {
  213.       Serial.print(controllerResponse[i], HEX);
  214.     }
  215.     Serial.println("");
  216.   }
  217. }
  218.  
  219.  
  220. void flushDisplay() {
  221.   //Read data from Display and do nothing, just clear the buffer
  222.   while (Serial2.available() > 0) {
  223.     byte dummy = Serial2.read();
  224.   }
  225. }
  226.  
  227. void readController() {
  228.   // Read byte from the controller and send to Display
  229.   if (Serial1.available())
  230.   {
  231.     parseController(Serial1.peek(), millis());
  232.     Serial2.write(Serial1.read());
  233.     whosTalking = 1;
  234.   }
  235. }
  236.  
  237. void readDisplay()  {
  238.   // Read byte from the Display and send to controller
  239.   if (Serial2.available())
  240.   {
  241.     parseDisplay(Serial2.peek());
  242.     Serial1.write(Serial2.read());
  243.     whosTalking = 0;
  244.   }
  245. }
  246.  
  247.  
  248. void parseDisplay(byte Dincomming) {
  249.   //shift bytes
  250.   DData[0] = DData[1];
  251.   DData[1] = Dincomming;
  252.  
  253.   if (LightExp) {
  254.     LightExp = false; //no longer expecting Light
  255.     if (DData[1] == 0xF1) {
  256.       light = true;
  257.     } else {
  258.       light = false;
  259.     }
  260.   }
  261.  
  262.   //check if first two bytes of setting PAS level are received, next two bytes will determine PAS level
  263.   if (memcmp ( DData, DPAS, sizeof(DData) ) == 0) {
  264.     DataExp = -1;
  265.     PASbyte = -1;
  266.     PASExp = true; // next two bytes for setting PAS level are expected
  267.   }
  268.   else if (memcmp ( DData, DLight, sizeof(DData) ) == 0) {
  269.     DataExp = -1;
  270.     nExpBytes = -1;
  271.     LightExp = true;
  272.   }
  273.   else if (memcmp ( DData, DBrake, sizeof(DData) ) == 0) {
  274.     DataExp = 4;
  275.     nExpBytes = 1;
  276.   }
  277.  
  278.   //Keep track of how many PAS-level bytes are received
  279.   if (PASExp) {
  280.     PASbyte = PASbyte + 1;
  281.   }
  282.  
  283.   //When two PASlevel bytes are received (after the 0x16, 0x0B mask), parse two PAS-level bytes
  284.   if (PASbyte == 2) {
  285.     PASExp = false; //no longer expecting PAS-level
  286.     PASbyte = -1;//reset
  287.     parsePAS();
  288.   }
  289.  
  290.   //when logging turned off, skip rest of parsing
  291.   if (!logging) {
  292.     return;
  293.   }
  294.  
  295.   //compare byte array 'DData' with known commands from display  
  296.   if (memcmp ( DData, DSpeed, sizeof(DData) ) == 0) {
  297.     DataExp = 0;
  298.     nExpBytes = 3;
  299.   }
  300.   else if (memcmp ( DData, DAmps, sizeof(DData) ) == 0) {
  301.     DataExp = 1;
  302.     nExpBytes = 2;
  303.   }
  304.   else if (memcmp ( DData, Dmoving, sizeof(DData) ) == 0) {
  305.     DataExp = 2;
  306.     nExpBytes = 2;
  307.   }
  308.   else if (memcmp ( DData, DBatPerc, sizeof(DData) ) == 0) {
  309.     DataExp = 3;
  310.     nExpBytes = 2;
  311.   }
  312.   else {
  313.     DataExp = -1;
  314.     nExpBytes = 0;
  315.   }
  316.  
  317. }
  318.  
  319. void parsePAS() {
  320.  
  321.   int oldPAS = PASLevel;
  322.  
  323.   //compare byte array with PAS levels
  324.   if (memcmp ( DData, DPAS0, sizeof(DData) ) == 0) {
  325.     PASLevel = 0;
  326.   }
  327.   else if (memcmp ( DData, DPAS1, sizeof(DData) ) == 0) {
  328.     PASLevel = 1;
  329.   }
  330.   else if (memcmp ( DData, DPAS2, sizeof(DData) ) == 0) {
  331.     PASLevel = 2;
  332.   }
  333.   else if (memcmp ( DData, DPAS3, sizeof(DData) ) == 0) {
  334.     PASLevel = 3;
  335.   }
  336.   else if (memcmp ( DData, DPAS4, sizeof(DData) ) == 0) {
  337.     PASLevel = 4;
  338.   }
  339.   else if (memcmp ( DData, DPAS5, sizeof(DData) ) == 0) {
  340.     PASLevel = 5;
  341.   }
  342.   else if (memcmp ( DData, DPAS6, sizeof(DData) ) == 0) {
  343.     PASLevel = 6;
  344.   }
  345.   else if (memcmp ( DData, DPAS7, sizeof(DData) ) == 0) {
  346.     PASLevel = 7;
  347.   }
  348.   else if (memcmp ( DData, DPAS8, sizeof(DData) ) == 0) {
  349.     PASLevel = 8;
  350.   }
  351.   else if (memcmp ( DData, DPAS9, sizeof(DData) ) == 0) {
  352.     PASLevel = 9;
  353.   }
  354.  
  355.  
  356.   // Only do if PAS level actually changed
  357.   if (PASLevel != oldPAS) {
  358.     // set command for re-initialising current PAS level (in case speed limit needs to be changed)
  359.     PASreInit[2] = DData[0];
  360.     PASreInit[3] = DData[1];
  361.  
  362.     //when logging turned off, skip procedure
  363.     if (!logging) {
  364.       return;
  365.     }
  366.  
  367.     //Echo to USB
  368.     Serial.print("*P");
  369.     Serial.print(PASLevel);
  370.     //Serial.print(",");
  371.     //Serial.print(millis());
  372.     Serial.print("*");
  373.   }
  374. }
  375.  
  376.  
  377. void parseController(byte Cincomming, unsigned long timestamp) {
  378.  
  379.   //skip procedure if no data is expected
  380.   if (DataExp == -1) {
  381.     byteNo = 0;
  382.     return;
  383.   }
  384.  
  385.   //timestamp when first byte was received
  386.   if (byteNo == 0) {
  387.     FirstByteTimestamp = timestamp;
  388.   }
  389.  
  390.   //if bytes are expected, place byte in array and increment byteNo
  391.   if (byteNo < nExpBytes) {
  392.     CData[byteNo] = Cincomming;
  393.     byteNo = byteNo + 1;
  394.   }
  395.  
  396.   if (nExpBytes == byteNo) {
  397.     if (DataExp == 4 && Cincomming == 0x01) {
  398.       brake = false;
  399.     } else {
  400.       brake = true;
  401.     }
  402.     byteNo = 0;//reset
  403.     DataExp = -1;//reset
  404.   }
  405.  
  406.   //when logging turned off, skip procedure
  407.   if (!logging) {
  408.     return;
  409.   }
  410.  
  411.   //when the number of expected bytes for a specific display command have been received, parse data
  412.   if (nExpBytes == byteNo) {
  413.     switch (DataExp) {
  414.       case 0: //speed/
  415.         Serial.print("*S");
  416.         Serial.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
  417.         //Serial.print(",");
  418.         //Serial.print(FirstByteTimestamp);
  419.         Serial.print("*");
  420.         break;
  421.       case 1: //amps
  422.         Serial.print("*A");
  423.         Serial.print(CData[0] / 2); // divided by two this seems to be the Amps value
  424.         //Serial.print(",");
  425.         //Serial.print(FirstByteTimestamp);
  426.         Serial.print("*");
  427.         break;
  428.       case 2: //moving
  429.         Serial.print("*M");
  430.         Serial.print(CData[0]); //30=stationary, 31=bike is moving
  431.         //Serial.print(",");
  432.         //Serial.print(FirstByteTimestamp);
  433.         Serial.print("*");
  434.         break;
  435.       case 3: //BatteryPercentage (?)
  436.         Serial.print("*B");
  437.         Serial.print(CData[0]);
  438.         //  Serial.print(",");
  439.         // Serial.print(FirstByteTimestamp);
  440.         Serial.print("*");
  441.         break;
  442.       case 4: //brake status
  443.         Serial.print("*U");
  444.         Serial.print(CData[0]);
  445.         // Serial.print(",");
  446.         //Serial.print(FirstByteTimestamp);
  447.         Serial.print("*");
  448.         break;
  449.     }
  450.     byteNo = 0;//reset
  451.     DataExp = -1;//reset
  452.   }
  453. }
  454.  
  455. void readTerminal()  {
  456.   // read commands received from terminal
  457.   if (Serial.available())
  458.   {
  459.     char terminal =  Serial.read();
  460.  
  461.     switch (terminal) {
  462.       case 'l'://switch to limited speed
  463.         changeSpeedLimitation(false);
  464.         break;
  465.       case 'h'://switch to unlimited speed
  466.         changeSpeedLimitation(true);
  467.         break;
  468.       case '0':     // turn logging off
  469.         logging = false;
  470.         break;
  471.       case '1':      // turn logging on
  472.         logging = true;
  473.         break;
  474.     }
  475.   }
  476. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement