diff --git a/ESPBMS.ino b/ESPBMS.ino index 0e774ce..83f41d3 100644 --- a/ESPBMS.ino +++ b/ESPBMS.ino @@ -4,6 +4,14 @@ static BLEUUID serviceUUID("0000ff00-0000-1000-8000-00805f9b34fb"); //xiaoxiang static BLEUUID charUUID_rx("0000ff01-0000-1000-8000-00805f9b34fb"); //xiaoxiang bms rx id static BLEUUID charUUID_tx("0000ff02-0000-1000-8000-00805f9b34fb"); //xiaoxiang bms tx id +typedef struct +{ + byte start; + byte type; + byte status; + byte dataLen; +} bmsPacketHeaderStruct; + void setup() { Serial.begin(115200); BLEDevice::init(""); // Initialize BLE device @@ -108,9 +116,9 @@ void loop() { static void MyNotifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify){ hexDump((char*)pData, length); - /*if(!bleCollectPacket((char *)pData, length)){ + if(!bleCollectPacket((char *)pData, length)){ Serial.println("ERROR: packet could not be collected."); -}*/ + } } void hexDump(const char *data, uint32_t dataSize) @@ -123,3 +131,222 @@ void hexDump(const char *data, uint32_t dataSize) } Serial.println(""); } + +bool bleCollectPacket(char *data, uint32_t dataSize) // reconstruct packet, called by notifyCallback function +{ + static uint8_t packetstate = 0; //0 - empty, 1 - first half of packet received, 2- second half of packet received + + // packet sizes: + // (packet ID 03) = 4 (header) + 23 + 2*N_NTCs + 2 (checksum) + 1 (stop) + // (packet ID 04) = 4 (header) + 2*NUM_CELLS + 2 (checksum) + 1 (stop) + static uint8_t packetbuff[4 + 2*25 + 2 + 1] = {0x0}; // buffer size suitable for up to 25 cells + + static uint32_t totalDataSize = 0; + bool retVal = false; + hexDump(data,dataSize); + + if(totalDataSize + dataSize > sizeof(packetbuff)){ + Serial.printf("ERROR: datasize is overlength."); + + Serial.println( + String("ERROR: datasize is overlength. ") + + String("allocated=") + + String(sizeof(packetbuff)) + + String(", size=") + + String(totalDataSize + dataSize) + ); + + totalDataSize = 0; + packetstate = 0; + + retVal = false; + } + else if (data[0] == 0xdd && packetstate == 0) // probably got 1st half of packet + { + Serial.println("PKT1"); + packetstate = 1; + for (uint8_t i = 0; i < dataSize; i++) + { + packetbuff[i] = data[i]; + } + totalDataSize = dataSize; + retVal = true; + + if (data[dataSize - 1] == 0x77) { + //its full packets + packetstate = 2; + } + } + else if (data[dataSize - 1] == 0x77 && packetstate == 1) //probably got 2nd half of the packet + { + Serial.println("PKT2"); + packetstate = 2; + for (uint8_t i = 0; i < dataSize; i++) + { + packetbuff[i + totalDataSize] = data[i]; + } + totalDataSize += dataSize; + retVal = true; + } + + if (packetstate == 2) //got full packet + { + Serial.println("PKT3"); + uint8_t packet[totalDataSize]; + memcpy(packet, packetbuff, totalDataSize); + + bmsProcessPacket(packet); //pass pointer to retrieved packet to processing function + packetstate = 0; + totalDataSize = 0; + retVal = true; + } + return retVal; +} + +bool bmsProcessPacket(byte *packet) +{ + + bool isValid = isPacketValid(packet); + + if (isValid != true) + { + Serial.println("Invalid packer received"); + return false; + } + + bmsPacketHeaderStruct *pHeader = (bmsPacketHeaderStruct *)packet; + byte *data = packet + sizeof(bmsPacketHeaderStruct); // TODO Fix this ugly hack + unsigned int dataLen = pHeader->dataLen; + + bool result = false; + + // find packet type (basic info or cell info) + switch (pHeader->type) + { + case 3: + { + // Process basic info + result = processBasicInfo(data, dataLen); + break; + } + + case 4: + { + // Process cell info + result = processCellInfo(data, dataLen); + break; + } + + default: + result = false; + Serial.printf("Unsupported packet type detected. Type: %d", pHeader->type); + } + + return result; +} + + +bool processBasicInfo(byte *data, unsigned int dataLen){ + uint16_t Volts = ((uint32_t)two_ints_into16(data[0], data[1])) * 10; // Resolution 10 mV -> convert to milivolts eg 4895 > 48950mV + int32_t Amps = ((int32_t)two_ints_into16(data[2], data[3])) * 10; // Resolution 10 mA -> convert to miliamps + + int32_t Watts = Volts * Amps / 1000000; // W + + uint16_t CapacityRemainAh = ((uint16_t)two_ints_into16(data[4], data[5])) * 10; + uint8_t CapacityRemainPercent = ((uint8_t)data[19]); + + uint16_t Temp1 = (((uint16_t)two_ints_into16(data[23], data[24])) - 2731); + uint16_t Temp2 = (((uint16_t)two_ints_into16(data[25], data[26])) - 2731); + + uint16_t BalanceCodeLow = (two_ints_into16(data[12], data[13])); + uint16_t BalanceCodeHigh = (two_ints_into16(data[14], data[15])); + uint8_t MosfetStatus = ((byte)data[20]); + + Serial.printf("Total voltage: %f\r\n", (float)Volts / 1000); + Serial.printf("Amps: %f\r\n", (float)Amps / 1000); + Serial.printf("CapacityRemainAh: %f\r\n", (float)CapacityRemainAh / 1000); + Serial.printf("CapacityRemainPercent: %d\r\n", CapacityRemainPercent); + Serial.printf("Temp1: %f\r\n", (float)Temp1 / 10); + Serial.printf("Temp2: %f\r\n", (float)Temp2 / 10); + Serial.printf("Balance Code Low: 0x%x\r\n", BalanceCodeLow); + Serial.printf("Balance Code High: 0x%x\r\n", BalanceCodeHigh); + Serial.printf("Mosfet Status: 0x%x\r\n", MosfetStatus); + + return true; +} + +bool processCellInfo(byte *data, unsigned int dataLen) +{ + uint16_t _cellSum; + uint16_t _cellMin = 5000; + uint16_t _cellMax = 0; + uint16_t _cellAvg; + uint16_t _cellDiff; + + uint8_t NumOfCells = dataLen / 2; // data contains 2 bytes per cell + + //go trough individual cells + for (byte i = 0; i < dataLen / 2; i++){ + int CellVolt = ((uint16_t)two_ints_into16(data[i * 2], data[i * 2 + 1])); // Resolution 1 mV + _cellSum += CellVolt; + if (CellVolt > _cellMax) + { + _cellMax = CellVolt; + } + if (CellVolt < _cellMin) + { + _cellMin = CellVolt; + } + + Serial.printf("Cell %d volt: %f\r\n", i,(float)CellVolt/1000); + } + + Serial.printf("Max cell volt: %f\r\n", (float)_cellMax / 1000); + Serial.printf("Min cell volt: %f\r\n", (float)_cellMin / 1000); + Serial.printf("Difference cell volt: %f\r\n", (float)_cellMax - _cellMin / 1000); + Serial.printf("Average cell volt: %f\r\n", (float)_cellSum / NumOfCells / 1000); + + return true; +} + +bool isPacketValid(byte *packet) //check if packet is valid +{ + if (packet == nullptr){ + return false; + } + + bmsPacketHeaderStruct *pHeader = (bmsPacketHeaderStruct *)packet; + int checksumPos = pHeader->dataLen + 2; // status + data len + data + + int offset = 2; // header 0xDD and command type are not in data length + + if (packet[0] != 0xDD){ + // start bit missing + return false; + } + + if (packet[offset + checksumPos + 2] != 0x77){ + // stop bit missing + return false; + } + + byte checksum = 0; + for (int i = 0; i < checksumPos; i++){ + checksum += packet[offset + i]; + } + checksum = ((checksum ^ 0xFF) + 1) & 0xFF; + + if (checksum != packet[offset + checksumPos + 1]){ + return false; + } + + return true; +} + +int16_t two_ints_into16(int highbyte, int lowbyte) // turns two bytes into a single long integer +{ + int16_t result = (highbyte); + result <<= 8; //Left shift 8 bits, + result = (result | lowbyte); //OR operation, merge the two + return result; +} diff --git a/datatypes.h b/datatypes.h deleted file mode 100644 index b337546..0000000 --- a/datatypes.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef mydatatypes_H_ -#define mydatatypes_H_ - -typedef struct -{ - byte start; - byte type; - byte status; - byte dataLen; -} bmsPacketHeaderStruct; - -typedef struct -{ - uint16_t Volts; // unit 1mV - int32_t Amps; // unit 1mA - int32_t Watts; // unit 1W - uint16_t CapacityRemainAh; - uint8_t CapacityRemainPercent; //unit 1% - uint16_t Temp1; //unit 0.1C - uint16_t Temp2; //unit 0.1C - uint16_t BalanceCodeLow; - uint16_t BalanceCodeHigh; - uint8_t MosfetStatus; - -} packBasicInfoStruct; - -typedef struct -{ - uint8_t NumOfCells; - uint16_t CellVolt[BMS_MAX_CELLS]; //cell 1 has index 0 :-/ - uint16_t CellMax; - uint16_t CellMin; - uint16_t CellDiff; // difference between highest and lowest - uint16_t CellAvg; -} packCellInfoStruct; - -/* -struct packEepromStruct -{ - uint16_t POVP; - uint16_t PUVP; - uint16_t COVP; - uint16_t CUVP; - uint16_t POVPRelease; - uint16_t PUVPRelease; - uint16_t COVPRelease; - uint16_t CUVPRelease; - uint16_t CHGOC; - uint16_t DSGOC; -}; - -#define STRINGBUFFERSIZE 300 -char stringBuffer[STRINGBUFFERSIZE]; -*/ - -#endif /* mydatatypes_H_ */