├── .gitignore ├── BMS.cpp ├── BMS.h ├── DalyBMS.cpp ├── DalyBMS.h ├── README.md └── examples ├── BasicRead └── BasicRead.ino └── DalyBasicRead └── DalyBasicRead.ino /.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /.idea/workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | /.idea -------------------------------------------------------------------------------- /BMS.cpp: -------------------------------------------------------------------------------- 1 | #ifndef BMS_CPP 2 | #define BMS_CPP 3 | 4 | //This is to define how you wish the code to send errors. Usually you would want it to send to the Serial Monitor. 5 | //For me I wanted it to send it to the Serial Monitor but in JSON format. 6 | #ifndef debug_msg 7 | #define debug_msg(error) Serial.println(F("{\"s\":\"bms_error\", \"error\": \"" error "\"}")); 8 | // \/\/\/\/ Enable to get errors on Serial Monitor. Make sure to comment out the line above, too. 9 | //#define debug_msg(error) Serial.println(F(error)); 10 | #endif 11 | 12 | #include "Arduino.h" 13 | #include "BMS.h" 14 | 15 | BMS::BMS() 16 | { 17 | } 18 | 19 | void BMS::begin(SoftwareSerial *serialPort) 20 | { 21 | BMSSerial = serialPort; 22 | clear(); 23 | #if BMS_DEBUG 24 | Serial.println("BMS DEBUG ENABLED"); 25 | #endif 26 | } 27 | 28 | bool BMS::requestResponse(uint16_t maxWait) 29 | { 30 | // Make sure any remaining data is flushed out. 31 | clear(); 32 | BMSSerial->listen(); 33 | #if BMS_DEBUG 34 | Serial.println("Starting request..."); 35 | #endif 36 | // Send the request 37 | BMSSerial->write(header, 2); 38 | BMSSerial->write(outdata.command); 39 | BMSSerial->write(outdata.len); 40 | if (outdata.len) 41 | BMSSerial->write(buffer, outdata.len); 42 | BMSSerial->write(outdata.checksumA); 43 | BMSSerial->write(outdata.checksumB); 44 | BMSSerial->write(end); 45 | BMSSerial->flush(); 46 | #if BMS_DEBUG 47 | Serial.println("Finished sending request..."); 48 | #endif 49 | delay(10); 50 | 51 | auto start = millis(); 52 | while (BMSSerial->peek() != 0xDD) 53 | { 54 | if (millis() > maxWait + start) 55 | { 56 | #if BMS_DEBUG 57 | Serial.println("Got no data from the BMS. Returning nothing."); 58 | #endif 59 | return false; 60 | } 61 | delay(10); 62 | } 63 | 64 | #if BMS_DEBUG 65 | Serial.print("Start: "); 66 | printHex(next()); 67 | Serial.println(); 68 | #else 69 | next(); 70 | #endif 71 | 72 | // Second byte echos command. Lets make sure it matches the original byte. 73 | indata.command = next(); 74 | #if BMS_DEBUG 75 | Serial.print("Command: "); 76 | printHex(indata.command); 77 | Serial.println(); 78 | #endif 79 | if (indata.command != outdata.command) 80 | { 81 | debug_msg("Ignorable error: Command byte does not match.") return false; 82 | } 83 | 84 | //Next byte is status. 85 | byte status = next(); 86 | #if BMS_DEBUG 87 | Serial.print("Status: "); 88 | printHex(status); 89 | Serial.println(); 90 | #endif 91 | 92 | if (!checkStatus(status)) 93 | return false; 94 | 95 | //Next byte is length 96 | indata.len = next(); 97 | #if BMS_DEBUG 98 | Serial.print("Length: "); 99 | printHex(indata.len); 100 | Serial.print("\nData:"); 101 | #endif 102 | // Now lets load the payload into the... well payload. 103 | for (int i = 0; i < indata.len; i++) 104 | { 105 | buffer[i] = next(); 106 | #if BMS_DEBUG 107 | printHex(buffer[i]); 108 | #endif 109 | } 110 | 111 | indata.checksumA = next(); 112 | indata.checksumB = next(); 113 | #if BMS_DEBUG 114 | printHex(indata.checksumA); 115 | printHex(indata.checksumB); 116 | #endif 117 | 118 | // make sure last byte is end byte 119 | byte end = next(); 120 | 121 | #if BMS_DEBUG 122 | printHex(end); 123 | #endif 124 | 125 | if (end != 0x77) 126 | { 127 | debug_msg("Fatal error: End byte does not match."); 128 | return false; 129 | } 130 | 131 | return true; 132 | } 133 | 134 | boolean BMS::update(uint16_t maxWait) 135 | { 136 | //Request cell data. 137 | outdata.command = 0x04; 138 | outdata.len = 0x00; 139 | outdata.checksumA = 0xFF; 140 | outdata.checksumB = 0xFC; 141 | bool cellSuccess = requestResponse(maxWait); 142 | 143 | if (indata.len != NUM_CELLS * 2) 144 | debug_msg("Fatal error: BMS returned no or incorrect data!") 145 | 146 | else if (cellSuccess) 147 | { 148 | cellMax = 0, cellMin = 100; 149 | for (int i = 0; i < (indata.len) / 2; i++) 150 | { 151 | byte highbyte = buffer[2 * i]; 152 | byte lowbyte = buffer[2 * i + 1]; 153 | 154 | uint16_t cellnow = (highbyte << 8) | lowbyte; 155 | cells[i] = cellnow / 1000.0f; 156 | if (cells[i] > cellMax) 157 | cellMax = cells[i]; 158 | if (cells[i] < cellMin) 159 | cellMin = cells[i]; 160 | } 161 | cellDiff = cellMax - cellMin; 162 | } 163 | else 164 | { 165 | debug_msg("Some error getting cell data."); 166 | } 167 | clear(); 168 | //Request pack data. 169 | outdata.command = 0x03; 170 | outdata.len = 0x00; 171 | outdata.checksumA = 0xFF; 172 | outdata.checksumB = 0xFD; 173 | bool mainSuccess = requestResponse(maxWait); 174 | 175 | if (mainSuccess) 176 | { 177 | balanceState = ((uint32_t)buffer[14]) << 24 | ((uint32_t)buffer[15]) << 16 | ((uint16_t)buffer[12]) << 8 | buffer[13]; 178 | 179 | uint16_t temp; 180 | 181 | temp = (buffer[0] << 8) | buffer[1]; 182 | packVoltage = temp / 100.0f; // convert to float and leave at 2 dec places 183 | 184 | temp = (buffer[2] << 8) | buffer[3]; 185 | packCurrent = temp / 100.0f; // convert to float and leave at 2 dec places 186 | 187 | if (buffer[22] != NUM_TEMP_PROBES) 188 | { 189 | Serial.print(F("Error: num of temp probes. Found: ")); 190 | Serial.print(buffer[22]); 191 | } 192 | 193 | for (byte i = 0; i < buffer[22]; i++) 194 | { 195 | temp = buffer[23 + 2 * i] << 8 | buffer[24 + 2 * i]; 196 | probes[i] = (temp - 2731) / 10.00f; 197 | } 198 | 199 | MOSFETStatus.charge = (buffer[20] % 2) == 1; 200 | MOSFETStatus.discharge = buffer[20] >= 2; 201 | } 202 | else 203 | { 204 | debug_msg("Some error getting pack data."); 205 | } 206 | 207 | return cellSuccess && mainSuccess; 208 | } 209 | 210 | boolean BMS::checkStatus(byte status) 211 | { 212 | switch (status) 213 | { 214 | case 0x80: 215 | debug_msg("BMS returned fail code..."); 216 | return false; 217 | case 0x00: 218 | return true; 219 | default: 220 | debug_msg("BMS returned unknown code: "); 221 | Serial.println(status); 222 | return false; 223 | } 224 | return false; 225 | } 226 | 227 | void BMS::printHex(byte x) 228 | { 229 | if (x < 16) 230 | { 231 | Serial.print("0"); 232 | } 233 | Serial.print(x, HEX); 234 | Serial.print(" "); 235 | } 236 | 237 | //Robust way to return the next byte from the port. 238 | byte BMS::next() 239 | { 240 | while (!BMSSerial->available()) 241 | ; 242 | return BMSSerial->read(); 243 | } 244 | void BMS::clear() 245 | { 246 | while (BMSSerial->available()) 247 | { 248 | BMSSerial->read(); 249 | } 250 | } 251 | #endif -------------------------------------------------------------------------------- /BMS.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include 3 | #ifndef BMS_H 4 | #define BMS_H 5 | 6 | // Input how many cells you expect to have 7 | #ifndef NUM_CELLS 8 | #define NUM_CELLS 21 9 | #endif 10 | 11 | #ifndef NUM_TEMP_PROBES 12 | #define NUM_TEMP_PROBES 4 13 | #endif 14 | 15 | #ifndef BMS_DEBUG 16 | #define BMS_DEBUG false 17 | #endif 18 | 19 | typedef struct 20 | { 21 | byte command; 22 | byte len; //Length of the payload. Does not include cls, id, or checksum bytes 23 | byte checksumA; //Given to us from module. Checked against the rolling calculated A/B checksums. 24 | byte checksumB; 25 | } BMSPacket; 26 | 27 | typedef struct 28 | { 29 | boolean charge; 30 | boolean discharge; 31 | } MOSFET_STATUS; 32 | 33 | const byte header[2] = {0xDD, 0xA5}; 34 | const byte end = 0x77; 35 | 36 | class BMS 37 | { 38 | public: 39 | BMS(); 40 | void begin(SoftwareSerial *serialPort); 41 | boolean update(uint16_t maxWait = 1000); 42 | 43 | float *getCells() { return cells; } 44 | uint32_t getBalanceState() { return balanceState; } 45 | float getPackVoltage() { return packVoltage; } 46 | float getPackCurrent() { return packCurrent; } 47 | float getCellMaxVoltage() { return cellMax; } 48 | float getCellMinVoltage() { return cellMin; } 49 | float getCellDiff() { return cellDiff; } 50 | byte getNumProbes() { return NUM_TEMP_PROBES; } 51 | float *getProbeData() { return probes; } 52 | MOSFET_STATUS getMOSFETStatus() { return MOSFETStatus; } 53 | 54 | private: 55 | SoftwareSerial *BMSSerial; 56 | BMSPacket outdata; 57 | BMSPacket indata; 58 | bool checkStatus(byte status); 59 | bool requestResponse(uint16_t maxWait); 60 | 61 | float cells[NUM_CELLS]; 62 | byte buffer[50]; 63 | uint32_t balanceState; 64 | float packVoltage; 65 | float packCurrent; 66 | float cellMax; 67 | float cellMin; 68 | float cellDiff; 69 | MOSFET_STATUS MOSFETStatus; 70 | float probes[NUM_TEMP_PROBES]; 71 | void printHex(byte x); 72 | void clear(); 73 | byte next(); 74 | }; 75 | #endif -------------------------------------------------------------------------------- /DalyBMS.cpp: -------------------------------------------------------------------------------- 1 | #ifndef DalyBMS_CPP 2 | #define DalyBMS_CPP 3 | 4 | //This is to define how you wish the code to send errors. Usually you would want it to send to the Serial Monitor. 5 | //For me I wanted it to send it to the Serial Monitor but in JSON format. 6 | #ifndef debug_msg 7 | //#define debug_msg(error) Serial.println(F("{\"s\":\"bms_error\", \"error\": \"" error "\"}")); 8 | #define debug_msg(error) Serial.println(F(error)); 9 | #endif 10 | 11 | #include "Arduino.h" 12 | #include "DalyBMS.h" 13 | 14 | DalyBMS::DalyBMS() 15 | { 16 | } 17 | 18 | void DalyBMS::begin(SoftwareSerial *serialPort) 19 | { 20 | BMSSerial = serialPort; 21 | clear(); 22 | #if BMS_DEBUG 23 | Serial.println("BMS DEBUG ENABLED"); 24 | #endif 25 | } 26 | 27 | bool DalyBMS::requestResponse(uint16_t maxWait) 28 | { 29 | // Make sure any remaining data is flushed out. 30 | clear(); 31 | BMSSerial->listen(); 32 | #if BMS_DEBUG 33 | Serial.println("Starting request..."); 34 | #endif 35 | //The goal is to send out something like "a5 40 97 08 00 00 00 00 00 00 00 00 84" (Taken from the first line 36 | // Send the header. This is the "Frame Head/Start Flag" and also the "Communication Module Address" (a5 and 40) 37 | BMSSerial->write(header, 2); 38 | // Send the command. This is "message id" in the "Communications content information" table. (you pick, something between 90 and 97) 39 | BMSSerial->write(outdata.command); 40 | // Looks like this is always 8. 41 | BMSSerial->write(8); 42 | for(byte i = 0; i < 8; i++) //From the dump.txt, looks like we need to send 8 zeros. 43 | BMSSerial->write((byte) 0); 44 | BMSSerial->write(outdata.checksum); //The last number, the checksum. 45 | // BMSSerial->write(end); //No end byte for DalyBMS 46 | BMSSerial->flush(); 47 | #if BMS_DEBUG 48 | Serial.println("Finished sending request..."); 49 | #endif 50 | delay(5); 51 | 52 | auto start = millis(); 53 | while (BMSSerial->peek() != header[1]) //Wait for the BMS to return a 0xA5 54 | { 55 | if (millis() > maxWait + start) 56 | { 57 | #if BMS_DEBUG 58 | Serial.println("Got no data from the BMS. Returning nothing."); 59 | #endif 60 | return false; 61 | } 62 | delay(10); 63 | } 64 | 65 | #if BMS_DEBUG 66 | Serial.print("Start: "); 67 | printHex(next()); 68 | Serial.println(); 69 | #else 70 | next(); //Remove that 0xA5 71 | #endif 72 | 73 | next(); //Next byte should be 0x01. No need to check it, however. 74 | 75 | // Second byte echos command. Lets make sure it matches the original byte. 76 | indata.command = next(); 77 | #if BMS_DEBUG 78 | Serial.print("Command: "); 79 | printHex(indata.command); 80 | Serial.println(); 81 | #endif 82 | if (indata.command != outdata.command) 83 | { 84 | debug_msg("Ignorable error: Command byte does not match.") return false; 85 | } 86 | 87 | //Next byte is length 88 | if(next() != 8) { 89 | //The length of data should always be 8. Something is wrong if this isn't true. 90 | debug_msg("Error: Data length not 8 bytes. (Line 91)") return false; 91 | } 92 | 93 | // Now lets load the payload into the... well payload. 94 | for (int i = 0; i < 8; i++) 95 | { 96 | buffer[i] = next(); 97 | #if BMS_DEBUG 98 | printHex(buffer[i]); 99 | #endif 100 | } 101 | 102 | indata.checksum = next(); 103 | #if BMS_DEBUG 104 | printHex(indata.checksum); 105 | #endif 106 | 107 | // make sure last byte is end byte 108 | // byte end = next(); No end byte for DalyBMS. 109 | return true; 110 | } 111 | 112 | boolean DalyBMS::update(uint16_t maxWait) 113 | { 114 | //Ok so DalyBMS does this a lot differently than the other BMS. This is where you should experiment with making requests. 115 | //Here is an example request. Lets get the data at 0x90. 116 | outdata.command = 0x90; 117 | outdata.checksum = 0x7D; //Stole this out of dump.txt. IT WILL CHANGE BASED ON EACH REQUEST. 118 | requestResponse(1000); 119 | Serial.println("Response: "); 120 | for(byte i = 0; i < 8; i++) { 121 | printHex(buffer[i]); 122 | } 123 | 124 | //Note - to get cell voltages you might have to make a new function like requestResponse. 125 | //This is because to get cell voltages you have to send a request and then read up to 96 bytes of data. 126 | //If you need help, of course ask, but try it yourself first. 127 | } 128 | 129 | void DalyBMS::printHex(byte x) 130 | { 131 | if (x < 16) 132 | { 133 | Serial.write('0'); 134 | } 135 | Serial.print(x, HEX); 136 | Serial.write(' '); 137 | } 138 | 139 | //Robust way to return the next byte from the port. 140 | byte DalyBMS::next() 141 | { 142 | while (!BMSSerial->available()) 143 | ; 144 | return BMSSerial->read(); 145 | } 146 | void DalyBMS::clear() 147 | { 148 | while (BMSSerial->available()) 149 | { 150 | BMSSerial->read(); 151 | } 152 | } 153 | #endif -------------------------------------------------------------------------------- /DalyBMS.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include 3 | #ifndef DalyBMS_H 4 | #define DalyBMS_H 5 | 6 | // Input how many cells you expect to have 7 | #ifndef NUM_CELLS 8 | #define NUM_CELLS 21 9 | #endif 10 | 11 | #ifndef NUM_TEMP_PROBES 12 | #define NUM_TEMP_PROBES 4 13 | #endif 14 | 15 | #ifndef BMS_DEBUG 16 | #define BMS_DEBUG false 17 | #endif 18 | 19 | typedef struct 20 | { 21 | byte command; 22 | byte checksum; //Given to us from module. Checked against the rolling calculated A/B checksums. 23 | } DalyBMSPacket; 24 | 25 | typedef struct 26 | { 27 | boolean charge; 28 | boolean discharge; 29 | } MOSFET_STATUS; 30 | 31 | const byte header[2] = {0xA5, 0x40}; 32 | const byte end = 0x77; 33 | 34 | class DalyBMS 35 | { 36 | public: 37 | DalyBMS(); 38 | void begin(SoftwareSerial *serialPort); 39 | boolean update(uint16_t maxWait = 1000); 40 | 41 | float *getCells() { return cells; } 42 | uint32_t getBalanceState() { return balanceState; } 43 | float getPackVoltage() { return packVoltage; } 44 | float getPackCurrent() { return packCurrent; } 45 | float getCellMaxVoltage() { return cellMax; } 46 | float getCellMinVoltage() { return cellMin; } 47 | float getCellDiff() { return cellDiff; } 48 | byte getNumProbes() { return NUM_TEMP_PROBES; } 49 | float *getProbeData() { return probes; } 50 | MOSFET_STATUS getMOSFETStatus() { return MOSFETStatus; } 51 | 52 | private: 53 | SoftwareSerial *BMSSerial; 54 | DalyBMSPacket outdata; 55 | DalyBMSPacket indata; 56 | bool checkStatus(byte status); 57 | bool requestResponse(uint16_t maxWait); 58 | 59 | float cells[NUM_CELLS]; 60 | byte buffer[8]; 61 | uint32_t balanceState; 62 | float packVoltage; 63 | float packCurrent; 64 | float cellMax; 65 | float cellMin; 66 | float cellDiff; 67 | MOSFET_STATUS MOSFETStatus; 68 | float probes[NUM_TEMP_PROBES]; 69 | void printHex(byte x); 70 | void clear(); 71 | byte next(); 72 | }; 73 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generic BMS Reader for Arduino 2 | Want to access all that juicy battery data from your BMS without having to use [computer software](https://www.lithiumbatterypcb.com/Computer%20operation%20software.rar) to read the info? 3 | Want to display all that data on an Arduino driven LCD or OLED? You have come to the right place. 4 | 5 | This is an Arduino library that communicates data from Generic Chinese BMSs. 6 | Might work with: 7 | 12 | Aka, if your board looks like this: 13 | 14 | ![Image of Generic BMS](https://www.lithiumbatterypcb.com/wp-content/uploads/2018/01/60V-Lithium-Ion-Battery-PCB.jpg) 15 | 16 | It is probably going to work. Mine was a 21s so it had two of the 16 pin JST connectors at the base. All cell counts should work. You can always check if the UART protocol matches the protocol linked at the bottom of the page. 17 | 18 |

Step 1: Wiring

19 | 20 | 1. Wire your BMS to the cells and to the load as normal. 21 | 1. Connect the Arduino 22 | 1. Connect the ground of the arduino to the ground of the BMS 23 | 1. Connect the RX and TX to any two pins on the Arduino 24 | 1. DO NOT CONNECT THE FOURTH PIN TO VCC. You may connect this pin (which supplies ~10.5 volts) to VIN if you wish to power the Arduino from the BMS. 25 | 26 | # Step 2: Setting up the code 27 | *Just copy the example code!* 28 | 1. Set the number of cells with "#define NUM_CELLS" 29 | 1. Set the number of temperature probes with "#define NUM_TEMP_PROBES" 30 | 1. Create a Software Serial object. 31 | 1. Inside void setup(), begin the Software Serial port at 9600 baud. 32 | 1. Inside void setup(), pass the software serial object to the BMS object. 33 | 34 | ```cpp 35 | #define BMS_RX_PIN 2 //pin the bms is reciving signals on, where we are sending. 36 | #define BMS_TX_PIN 3 //pin the BMS is sending signals to, where we are recieving. 37 | #define NUM_CELLS 21 38 | #define NUM_TEMP_PROBES 4 39 | 40 | #include 41 | #include 42 | SoftwareSerial BMSSerial(BMS_TX_PIN, BMS_RX_PIN); 43 | BMS bms; 44 | 45 | void setup() { 46 | Serial.begin(115200); 47 | 48 | BMSSerial.begin(9600); 49 | bms.begin(&BMSSerial); 50 | 51 | Serial.println("Finished setup"); 52 | } 53 | ``` 54 | # Step 3: Reading the Data 55 | 1. Call "bms.update()" 56 | 1. Call the appropriate methods to return the relevant data: 57 | ```cpp 58 | float *getCells() { return cells; } 59 | uint32_t getBalanceState() { return balanceState; } 60 | float getPackVoltage() { return packVoltage; } 61 | float getPackCurrent() { return packCurrent; } 62 | float getCellMaxVoltage() { return cellMax; } 63 | float getCellMinVoltage() { return cellMin; } 64 | float getCellDiff() { return cellDiff; } 65 | byte getNumProbes() { return NUM_TEMP_PROBES; } 66 | float *getProbeData() { return probes; } 67 | MOSFET_STATUS getMOSFETStatus() { return MOSFETStatus; } 68 | ``` 69 | Note about using the MOSFETStatus struct: just do "bms.getMOSFETStatus().discharge" to get whether discharge is permitted and "bms.getMOSFETStatus().charge" to get whether charger is permitted. 70 | 71 | 72 | Sources and Helpful Links 73 | 1. Another [Smart BMS Reader](https://github.com/bres55/Smart-BMS-arduino-Reader) designed for a similar purpose. I would have used this version if it had been in the traditional Arduino Library format (import, instantiate, start, call methods...), or easier to understand. 74 | 1. My [copy of the spreadsheet](https://docs.google.com/spreadsheets/d/1XLCGFEuwLSbxoiXvt5f8QokEm06ZbBmTDPy41bfProQ/edit?usp=sharing) defining the BMS Protocol that explains the protocol and how to communicate with the BMS. I also color coded one of the responses so you can see what bytes mean what. 75 | 1. [Any other information you might ever need](https://web.archive.org/web/20210325200036/https://wnsnty.xyz/entry/jbd-xiaoyang-smart-bluetooth-bms-information) plus a TON of helpful links that I won't duplicate. 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /examples/BasicRead/BasicRead.ino: -------------------------------------------------------------------------------- 1 | /** 2 | This code will: 3 | - Read the BMS cell voltages, pack voltage, and error codes. 4 | */ 5 | 6 | #define BMS_RX_PIN 2 //pin the bms is receiving signals on, where we are sending. 7 | #define BMS_TX_PIN 3 //pin the BMS is sending signals to, where we are receiving. 8 | #define NUM_CELLS 21 9 | #define NUM_TEMP_PROBES 4 10 | #define BMS_DEBUG false 11 | // Debug mode prints the hex of every byte coming in and leaving. 12 | // You can compare the bytes you receive to what you should be receiving on the google sheet linked at the bottom of the readme. 13 | 14 | #include 15 | #include 16 | SoftwareSerial BMSSerial(BMS_TX_PIN, BMS_RX_PIN); 17 | BMS bms; 18 | 19 | void setup() 20 | { 21 | Serial.begin(115200); 22 | 23 | BMSSerial.begin(9600); 24 | bms.begin(&BMSSerial); 25 | 26 | Serial.println("Finished setup"); 27 | } 28 | 29 | void loop() 30 | { 31 | 32 | bms.update(); 33 | Serial.println("Voltages: (V)"); 34 | for (byte i = 0; i < NUM_CELLS; i++) 35 | { 36 | Serial.print(i + 1); 37 | Serial.print(F(" ")); 38 | if (i < 9) 39 | Serial.print(F(" ")); 40 | } 41 | Serial.println(); 42 | float *cells = bms.getCells(); 43 | for (byte i = 0; i < NUM_CELLS; i++) 44 | { 45 | Serial.print(cells[i], 2); 46 | Serial.print(F(" ")); 47 | } 48 | Serial.println(); 49 | uint32_t balstate = bms.getBalanceState(); 50 | for (byte i = 0; i < NUM_CELLS; i++) 51 | { 52 | Serial.print(bitRead(balstate, i)); 53 | Serial.print(F(" ")); 54 | } 55 | 56 | Serial.print("\nPack voltage: "); 57 | Serial.println(bms.getPackVoltage()); 58 | Serial.println("\nMax Min Average"); 59 | Serial.print(bms.getCellMaxVoltage()); 60 | Serial.print(" "); 61 | Serial.print(bms.getCellMinVoltage()); 62 | Serial.print(" "); 63 | Serial.println(bms.getPackVoltage() / NUM_CELLS); 64 | Serial.print("Cell Diff: "); 65 | Serial.println(bms.getCellDiff()); 66 | 67 | Serial.print("Charge: "); 68 | Serial.println(bms.getMOSFETStatus().charge ? "Permitted" : "Prohibited"); 69 | Serial.print("Discharge: "); 70 | Serial.println(bms.getMOSFETStatus().discharge ? "Permitted" : "Prohibited"); 71 | 72 | Serial.println("Temps: (C)"); 73 | for (byte i = 0; i < NUM_TEMP_PROBES; i++) 74 | { 75 | Serial.print(i + 1); 76 | Serial.print(F(" ")); 77 | } 78 | Serial.println(); 79 | float *probes = bms.getProbeData(); 80 | for (byte i = 0; i < NUM_TEMP_PROBES; i++) 81 | { 82 | Serial.print(probes[i], 2); 83 | Serial.print(F(" ")); 84 | } 85 | 86 | delay(2000); 87 | } 88 | -------------------------------------------------------------------------------- /examples/DalyBasicRead/DalyBasicRead.ino: -------------------------------------------------------------------------------- 1 | /** 2 | This code will: 3 | - Read the BMS cell voltages, pack voltage, and error codes 4 | - Work on DalyBMS 5 | */ 6 | 7 | #define BMS_RX_PIN 2 //pin the bms is receiving signals on, where we are sending. 8 | #define BMS_TX_PIN 3 //pin the BMS is sending signals to, where we are receiving. 9 | #define NUM_CELLS 21 10 | #define NUM_TEMP_PROBES 4 11 | #define BMS_DEBUG false 12 | // Debug mode prints the hex of every byte coming in and leaving. 13 | // You can compare the bytes you receive to what you should be receiving on the google sheet linked at the bottom of the readme. 14 | 15 | #include 16 | #include 17 | SoftwareSerial BMSSerial(BMS_TX_PIN, BMS_RX_PIN); 18 | DalyBMS bms; 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | 24 | BMSSerial.begin(9600); 25 | bms.begin(&BMSSerial); 26 | 27 | Serial.println("Finished setup"); 28 | 29 | bms.update(); //Put this loop eventually. For testing run it once. 30 | } 31 | 32 | void loop() 33 | { 34 | } 35 | --------------------------------------------------------------------------------