├── .vscode ├── arduino.json ├── c_cpp_properties.json └── settings.json ├── 1.jpg ├── 2.jpg ├── BLE.ino ├── BMS_process_data.ino ├── LCD.ino ├── README.md ├── RGB-LED.ino ├── Smart-BMS-Bluetooth-ESP32.ino ├── User_Setup-arduitouch.h ├── User_Setup-bigger_display_ttgo.h ├── User_Setup-smaller_display_ttgo.h ├── User_Setup-t-beam.h ├── color_magic.ino ├── index.html ├── mydatatypes.h ├── network.ino ├── oled.ino ├── simulation.ino └── webpages.h /.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "sketch": "Smart-BMS-Bluetooth-ESP32.ino", 3 | "board": "esp32:esp32:esp32", 4 | "configuration": "PSRAM=disabled,PartitionScheme=min_spiffs,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none", 5 | "port": "COM20", 6 | "output": "../build" 7 | } -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "C:\\\\work\\\\arduino-PR-beta1.9-BUILD-105\\\\portable\\\\packages\\\\esp32\\\\tools\\\\**", 7 | "C:\\\\work\\\\arduino-PR-beta1.9-BUILD-105\\\\portable\\\\packages\\\\esp32\\\\hardware\\\\esp32\\\\1.0.1\\\\**", 8 | "C:\\\\work\\\\arduino-PR-beta1.9-BUILD-105\\\\portable\\\\sketchbook\\\\libraries\\\\**", 9 | "C:\\d_drive\\private\\work\\arduino-1.8.8\\portable\\packages\\esp32\\tools\\**", 10 | "C:\\d_drive\\private\\work\\arduino-1.8.8\\portable\\packages\\esp32\\hardware\\esp32\\1.0.1\\**", 11 | "${workspaceFolder}/**", 12 | "D:\\private\\work\\arduino-1.8.8\\portable\\sketchbook\\libraries\\**", 13 | "D:\\private\\work\\arduino-1.8.8\\portable\\packages\\esp32\\hardware\\esp32\\**", 14 | "D:\\private\\work\\arduino-1.8.8\\portable\\packages\\esp32\\tools\\**", 15 | "D:\\private\\work\\arduino-1.8.8\\portable\\sketchbook\\libraries\\**" 16 | ], 17 | "forcedInclude": [], 18 | "intelliSenseMode": "msvc-x64", 19 | 20 | "cStandard": "c11", 21 | "cppStandard": "c++17" 22 | } 23 | ], 24 | "version": 4 25 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "system_error": "cpp", 4 | "type_traits": "cpp", 5 | "cmath": "cpp", 6 | "cstdlib": "cpp" 7 | } 8 | } -------------------------------------------------------------------------------- /1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kolins-cz/Smart-BMS-Bluetooth-ESP32/39d5f9dc71bdaabd6753d9bd1a5f69f2aaa65b94/1.jpg -------------------------------------------------------------------------------- /2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kolins-cz/Smart-BMS-Bluetooth-ESP32/39d5f9dc71bdaabd6753d9bd1a5f69f2aaa65b94/2.jpg -------------------------------------------------------------------------------- /BLE.ino: -------------------------------------------------------------------------------- 1 | // ----- BLE stuff ----- 2 | static BLERemoteCharacteristic *pRemoteCharacteristic; 3 | static BLEAdvertisedDevice *myDevice; 4 | BLERemoteService *pRemoteService; 5 | // The remote service we wish to connect to. Needs check/change when other BLE module used. 6 | static BLEUUID serviceUUID("0000ff00-0000-1000-8000-00805f9b34fb"); //xiaoxiang bms original module 7 | static BLEUUID charUUID_tx("0000ff02-0000-1000-8000-00805f9b34fb"); //xiaoxiang bms original module 8 | static BLEUUID charUUID_rx("0000ff01-0000-1000-8000-00805f9b34fb"); //xiaoxiang bms original module 9 | // 0000ff01-0000-1000-8000-00805f9b34fb 10 | // NOTIFY, READ 11 | // Notifications from this characteristic is received data from BMS 12 | 13 | // 0000ff02-0000-1000-8000-00805f9b34fb 14 | // Write this characteristic to send data to BMS 15 | // READ, WRITE, WRITE NO RESPONSE 16 | 17 | class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks 18 | { //this is called by some underlying magic 19 | /** 20 | * Called for each advertising BLE server. 21 | */ 22 | void onResult(BLEAdvertisedDevice advertisedDevice) 23 | { 24 | commSerial.print("BLE Advertised Device found: "); 25 | commSerial.println(advertisedDevice.toString().c_str()); 26 | 27 | // We have found a device, let us now see if it contains the service we are looking for. 28 | if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) 29 | { 30 | 31 | BLEDevice::getScan()->stop(); 32 | myDevice = new BLEAdvertisedDevice(advertisedDevice); 33 | doConnect = true; 34 | doScan = true; 35 | 36 | } // Found our server 37 | } // onResult 38 | }; // MyAdvertisedDeviceCallbacks 39 | class MyClientCallback : public BLEClientCallbacks 40 | { //this is called on connect / disconnect by some underlying magic+ 41 | 42 | void onConnect(BLEClient *pclient) 43 | { 44 | } 45 | 46 | void onDisconnect(BLEClient *pclient) 47 | { 48 | BLE_client_connected = false; 49 | commSerial.println("onDisconnect"); 50 | lcdDisconnect(); 51 | } 52 | }; 53 | 54 | void bleRequestData() 55 | { 56 | #ifndef SIMULATION 57 | 58 | // If the flag "doConnect" is true then we have scanned for and found the desired 59 | // BLE Server with which we wish to connect. Now we connect to it. Once we are 60 | // connected we set the connected flag to be true. 61 | if (doConnect == true) 62 | { 63 | if (connectToServer()) 64 | { 65 | commSerial.println("We are now connected to the BLE Server."); 66 | lcdConnected(); 67 | } 68 | else 69 | { 70 | lcdConnectionFailed(); 71 | } 72 | doConnect = false; 73 | } 74 | 75 | // If we are connected to a peer BLE Server, update the characteristic each time we are reached 76 | // with the current time since boot. 77 | if (BLE_client_connected == true) 78 | { 79 | 80 | unsigned long currentMillis = millis(); 81 | if ((currentMillis - previousMillis >= interval || newPacketReceived)) //every time period or when packet is received 82 | { 83 | previousMillis = currentMillis; 84 | showInfoLcd(); 85 | 86 | if (toggle) //alternate info3 and info4 87 | { 88 | bmsGetInfo3(); 89 | //showBasicInfo(); 90 | newPacketReceived = false; 91 | } 92 | else 93 | { 94 | bmsGetInfo4(); 95 | //showCellInfo(); 96 | newPacketReceived = false; 97 | } 98 | toggle = !toggle; 99 | } 100 | } 101 | else if (doScan) 102 | { 103 | BLEDevice::getScan()->start(0); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino 104 | } 105 | #endif 106 | 107 | #ifdef SIMULATION 108 | bmsSimulate(); 109 | #endif 110 | } 111 | 112 | void bleStartup() 113 | { 114 | #ifndef SIMULATION 115 | 116 | TRACE; 117 | BLEDevice::init(""); 118 | 119 | // Retrieve a Scanner and set the callback we want to use to be informed when we 120 | // have detected a new device. Specify that we want active scanning and start the 121 | // scan to run for 5 seconds. 122 | BLEScan *pBLEScan = BLEDevice::getScan(); 123 | pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); 124 | pBLEScan->setInterval(1349); 125 | pBLEScan->setWindow(449); 126 | pBLEScan->setActiveScan(true); 127 | pBLEScan->start(5, true); 128 | #endif 129 | } 130 | 131 | static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) //this is called when BLE server sents data via notofication 132 | { 133 | TRACE; 134 | //hexDump((char*)pData, length); 135 | bleCollectPacket((char *)pData, length); 136 | } 137 | 138 | bool connectToServer() 139 | { 140 | TRACE; 141 | commSerial.print("Forming a connection to "); 142 | lcdConnectingStatus(0); 143 | commSerial.println(myDevice->getAddress().toString().c_str()); 144 | BLEClient *pClient = BLEDevice::createClient(); 145 | commSerial.println(" - Created client"); 146 | lcdConnectingStatus(1); 147 | pClient->setClientCallbacks(new MyClientCallback()); 148 | 149 | // Connect to the remove BLE Server. 150 | pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private) 151 | commSerial.println(" - Connected to server"); 152 | lcdConnectingStatus(2); 153 | // Obtain a reference to the service we are after in the remote BLE server. 154 | //BLERemoteService* 155 | pRemoteService = pClient->getService(serviceUUID); 156 | if (pRemoteService == nullptr) 157 | { 158 | commSerial.print("Failed to find our service UUID: "); 159 | lcdConnectingStatus(3); 160 | commSerial.println(serviceUUID.toString().c_str()); 161 | pClient->disconnect(); 162 | return false; 163 | } 164 | commSerial.println(" - Found our service"); 165 | lcdConnectingStatus(4); 166 | 167 | // Obtain a reference to the characteristic in the service of the remote BLE server. 168 | pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID_rx); 169 | if (pRemoteCharacteristic == nullptr) 170 | { 171 | commSerial.print("Failed to find our characteristic UUID: "); 172 | lcdConnectingStatus(5); 173 | commSerial.println(charUUID_rx.toString().c_str()); 174 | pClient->disconnect(); 175 | return false; 176 | } 177 | commSerial.println(" - Found our characteristic"); 178 | lcdConnectingStatus(6); 179 | // Read the value of the characteristic. 180 | if (pRemoteCharacteristic->canRead()) 181 | { 182 | String value = pRemoteCharacteristic->readValue(); // geändert wegen Fehlermeldung "conversion from 'String' to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string'} requested" 183 | commSerial.print("The characteristic value was: "); 184 | commSerial.println(value.c_str()); 185 | } 186 | 187 | if (pRemoteCharacteristic->canNotify()) 188 | pRemoteCharacteristic->registerForNotify(notifyCallback); 189 | 190 | BLE_client_connected = true; 191 | return true; // geändert wegen Warnmeldung Rückkehr ohne Wert non void 192 | } 193 | 194 | void sendCommand(uint8_t *data, uint32_t dataLen) 195 | { 196 | TRACE; 197 | 198 | pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID_tx); 199 | 200 | if (pRemoteCharacteristic) 201 | { 202 | pRemoteCharacteristic->writeValue(data, dataLen); 203 | //commSerial.println("bms request sent"); 204 | } 205 | else 206 | { 207 | commSerial.println("Remote TX characteristic not found"); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /BMS_process_data.ino: -------------------------------------------------------------------------------- 1 | bool isPacketValid(byte *packet) //check if packet is valid 2 | { 3 | TRACE; 4 | if (packet == nullptr) 5 | { 6 | return false; 7 | } 8 | 9 | bmsPacketHeaderStruct *pHeader = (bmsPacketHeaderStruct *)packet; 10 | int checksumLen = pHeader->dataLen + 2; // status + data len + data 11 | 12 | if (pHeader->start != 0xDD) 13 | { 14 | return false; 15 | } 16 | 17 | int offset = 2; // header 0xDD and command type are skipped 18 | 19 | byte checksum = 0; 20 | for (int i = 0; i < checksumLen; i++) 21 | { 22 | checksum += packet[offset + i]; 23 | } 24 | 25 | //printf("checksum: %x\n", checksum); 26 | 27 | checksum = ((checksum ^ 0xFF) + 1) & 0xFF; 28 | //printf("checksum v2: %x\n", checksum); 29 | 30 | byte rxChecksum = packet[offset + checksumLen + 1]; 31 | 32 | if (checksum == rxChecksum) 33 | { 34 | //printf("Packet is valid\n"); 35 | return true; 36 | } 37 | else 38 | { 39 | //printf("Packet is not valid\n"); 40 | //printf("Expected value: %x\n", rxChecksum); 41 | return false; 42 | } 43 | } 44 | 45 | bool processBasicInfo(packBasicInfoStruct *output, byte *data, unsigned int dataLen) 46 | { 47 | TRACE; 48 | output->Volts = ((uint32_t)two_ints_into16(data[0], data[1])) * 10; // Resolution 10 mV -> convert to milivolts eg 4895 > 48950mV 49 | output->Amps = ((int32_t)two_ints_into16(data[2], data[3])) * 10; // Resolution 10 mA -> convert to miliamps 50 | 51 | output->Watts = output->Volts * output->Amps / 1000000; // W 52 | output->CapacityRemainAh = ((uint32_t)two_ints_into16(data[4], data[5])) * 10;// auf32bit erweitert wege Überlauf 53 | 54 | output->CapacityRemainWh = (output->CapacityRemainAh * c_cellNominalVoltage) / 1000000 * packCellInfo.NumOfCells; 55 | 56 | output->BalanceCodeLow = (two_ints_into16(data[12], data[13])); 57 | output->BalanceCodeHigh = (two_ints_into16(data[14], data[15])); 58 | output->CapacityRemainPercent = ((uint8_t)data[19]); 59 | output->MosfetStatus = ((byte)data[20]); 60 | uint8_t tempCount = ((byte)data[22]); 61 | switch (tempCount) 62 | { 63 | case 0: 64 | output->Temp1 = 0; 65 | output->Temp2 = 0; 66 | break; 67 | case 1: 68 | output->Temp1 = (((uint16_t)two_ints_into16(data[23], data[24])) - 2731); 69 | output->Temp2 = 0; 70 | break; 71 | case 2: 72 | output->Temp1 = (((uint16_t)two_ints_into16(data[23], data[24])) - 2731); 73 | output->Temp2 = (((uint16_t)two_ints_into16(data[24], data[26])) - 2731); 74 | break; 75 | default: 76 | // We do not support more than 2 temperature senors 77 | break; 78 | } 79 | 80 | uint8_t offset = tempCount * 2; // Each temperature sensor needs 2 bytes, so everything after this is offset 81 | if (dataLen == 32 + offset) // New BMS Firmware with more parameters 82 | { 83 | output->Humidity = ((byte)data[23 + offset]); 84 | output->AlarmStatus = (two_ints_into16(data[24 + offset], data[25 + offset])); 85 | output->FullChargeCapacity = (two_ints_into16(data[26 + offset], data[27 + offset])) * 10; 86 | output->RemainingCapacity = ((uint32_t)two_ints_into16(data[28 + offset], data[29 + offset])) * 10; 87 | output->BalanceCurrent = (two_ints_into16(data[30 + offset], data[31 + offset])); // Resolution 1 mA 88 | } 89 | return true; 90 | }; 91 | 92 | bool processCellInfo(packCellInfoStruct *output, byte *data, unsigned int dataLen) 93 | { 94 | 95 | TRACE; 96 | uint16_t _cellSum; 97 | uint16_t _cellMin = 5000; 98 | uint16_t _cellMax = 0; 99 | uint16_t _cellAvg; 100 | uint16_t _cellDiff; 101 | 102 | output->NumOfCells = dataLen / 2; // Data length * 2 is number of cells !!!!!! 103 | 104 | //go trough individual cells 105 | for (byte i = 0; i < dataLen / 2; i++) 106 | { 107 | output->CellVolt[i] = ((uint16_t)two_ints_into16(data[i * 2], data[i * 2 + 1])); // Resolution 1 mV 108 | _cellSum += output->CellVolt[i]; 109 | if (output->CellVolt[i] > _cellMax) 110 | { 111 | _cellMax = output->CellVolt[i]; 112 | } 113 | if (output->CellVolt[i] < _cellMin) 114 | { 115 | _cellMin = output->CellVolt[i]; 116 | } 117 | 118 | output->CellColor[i] = getPixelColorHsv(mapHue(output->CellVolt[i], c_cellAbsMin, c_cellAbsMax), 255, 255); 119 | } 120 | output->CellMin = _cellMin; 121 | output->CellMax = _cellMax; 122 | output->CellDiff = _cellMax - _cellMin; // Resolution 10 mV -> convert to volts 123 | output->CellAvg = _cellSum / output->NumOfCells; 124 | 125 | //----cell median calculation---- 126 | uint16_t n = output->NumOfCells; 127 | uint16_t i, j; 128 | uint16_t temp; 129 | uint16_t x[n]; 130 | 131 | for (uint8_t u = 0; u < n; u++) 132 | { 133 | x[u] = output->CellVolt[u]; 134 | } 135 | 136 | for (i = 1; i <= n; ++i) //sort data 137 | { 138 | for (j = i + 1; j <= n; ++j) 139 | { 140 | if (x[i] > x[j]) 141 | { 142 | temp = x[i]; 143 | x[i] = x[j]; 144 | x[j] = temp; 145 | } 146 | } 147 | } 148 | 149 | if (n % 2 == 0) //compute median 150 | { 151 | output->CellMedian = (x[n / 2] + x[n / 2 + 1]) / 2; 152 | } 153 | else 154 | { 155 | output->CellMedian = x[n / 2 + 1]; 156 | } 157 | 158 | for (uint8_t q = 0; q < output->NumOfCells; q++) 159 | { 160 | uint32_t disbal = abs(output->CellMedian - output->CellVolt[q]); 161 | output->CellColorDisbalance[q] = getPixelColorHsv(mapHue(disbal, c_cellMaxDisbalance, 0), 255, 255); 162 | } 163 | return true; 164 | }; 165 | 166 | bool bmsProcessPacket(byte *packet) 167 | { 168 | TRACE; 169 | bool isValid = isPacketValid(packet); 170 | 171 | if (isValid != true) 172 | { 173 | commSerial.println("Invalid packer received"); 174 | return false; 175 | } 176 | 177 | bmsPacketHeaderStruct *pHeader = (bmsPacketHeaderStruct *)packet; 178 | byte *data = packet + sizeof(bmsPacketHeaderStruct); // TODO Fix this ugly hack 179 | unsigned int dataLen = pHeader->dataLen; 180 | 181 | bool result = false; 182 | 183 | // |Decision based on pac ket type (info3 or info4) 184 | switch (pHeader->type) 185 | { 186 | case cBasicInfo3: 187 | { 188 | // Process basic info 189 | result = processBasicInfo(&packBasicInfo, data, dataLen); 190 | newPacketReceived = true; 191 | break; 192 | } 193 | 194 | case cCellInfo4: 195 | { 196 | result = processCellInfo(&packCellInfo, data, dataLen); 197 | newPacketReceived = true; 198 | break; 199 | } 200 | 201 | default: 202 | result = false; 203 | commSerial.printf("Unsupported packet type detected. Type: %d", pHeader->type); 204 | } 205 | 206 | return result; 207 | } 208 | 209 | bool bmsCollectPacket_uart(byte *packet) //unused function to get packet directly from uart 210 | { 211 | TRACE; 212 | #define packet1stByte 0xdd 213 | #define packet2ndByte 0x03 214 | #define packet2ndByte_alt 0x04 215 | #define packetLastByte 0x77 216 | bool retVal; 217 | byte actualByte; 218 | static byte previousByte; 219 | static bool inProgress = false; 220 | static byte bmsPacketBuff[40]; 221 | static byte bmsPacketBuffCount; 222 | 223 | if (bmsSerial.available() > 0) //data in serial buffer available 224 | { 225 | actualByte = bmsSerial.read(); 226 | if (previousByte == packetLastByte && actualByte == packet1stByte) //got packet footer 227 | { 228 | memcpy(packet, bmsPacketBuff, bmsPacketBuffCount); 229 | inProgress = false; 230 | retVal = true; 231 | } 232 | if (inProgress) //filling bytes to output buffer 233 | { 234 | bmsPacketBuff[bmsPacketBuffCount] = actualByte; 235 | bmsPacketBuffCount++; 236 | retVal = false; 237 | } 238 | 239 | if (previousByte == packet1stByte && (actualByte == packet2ndByte || actualByte == packet2ndByte_alt)) //got packet header 240 | { 241 | bmsPacketBuff[0] = previousByte; 242 | bmsPacketBuff[1] = actualByte; 243 | bmsPacketBuffCount = 2; // for next pass. [0] and [1] are filled already 244 | inProgress = true; 245 | retVal = false; 246 | } 247 | 248 | previousByte = actualByte; 249 | } 250 | else 251 | { 252 | retVal = false; 253 | } 254 | return retVal; 255 | } 256 | 257 | 258 | bool bleCollectPacket(char *data, uint32_t dataSize) // reconstruct packet from BLE incomming data, called by notifyCallback function 259 | { 260 | TRACE; 261 | static uint8_t packetstate = 0; //0 - empty, 1 - first half of packet received, 2- second half of packet received, 3 last packet 262 | static uint8_t packetbuff[100] = {0x0}; 263 | static uint32_t previousDataSize = 0; 264 | 265 | static uint32_t newDataSize = 0; 266 | bool retVal = false; 267 | // hexDump(data, dataSize); 268 | 269 | if (data[0] == 0xdd && packetstate == 0) // probably get the 1st part of the package 270 | { 271 | packetstate = 1; 272 | previousDataSize = dataSize; 273 | 274 | for (uint8_t i = 0; i < dataSize; i++) 275 | { 276 | packetbuff[i] = data[i]; 277 | } 278 | retVal = false; 279 | } 280 | 281 | if (data[dataSize - 1] == 0x77 && packetstate >= 1) // probably got last packet 282 | { 283 | packetstate = 3; 284 | 285 | for (uint8_t i = 0; i < dataSize; i++) 286 | { 287 | packetbuff[i + previousDataSize] = data[i]; 288 | } 289 | retVal = false; 290 | } 291 | else 292 | { 293 | if (data[0] != 0xDD ) // probably get a next part of the package 294 | { 295 | 296 | packetstate = 2; 297 | 298 | newDataSize = previousDataSize; 299 | for (uint8_t i = 0; i < previousDataSize; i++) 300 | { 301 | packetbuff[i + previousDataSize] = data[i]; 302 | } 303 | previousDataSize = newDataSize + dataSize ; 304 | retVal = false; 305 | } 306 | } 307 | 308 | if (packetstate == 3) //got full packet 309 | { 310 | uint8_t packet[dataSize + previousDataSize]; 311 | memcpy(packet, packetbuff, dataSize + previousDataSize); 312 | bmsProcessPacket(packet); //pass pointer to retrieved packet to processing function 313 | packetstate = 0; 314 | newDataSize = 0; 315 | previousDataSize = 0; 316 | retVal = true; 317 | } 318 | return retVal; 319 | } 320 | 321 | void bmsGetInfo3() 322 | { 323 | TRACE; 324 | // header status command length data checksum footer 325 | // DD A5 03 00 FF FD 77 326 | uint8_t data[7] = {0xdd, 0xa5, 0x3, 0x0, 0xff, 0xfd, 0x77}; 327 | //bmsSerial.write(data, 7); 328 | sendCommand(data, sizeof(data)); 329 | //commSerial.println("Request info3 sent"); 330 | } 331 | 332 | void bmsGetInfo4() 333 | { 334 | TRACE; 335 | // DD A5 04 00 FF FC 77 336 | uint8_t data[7] = {0xdd, 0xa5, 0x4, 0x0, 0xff, 0xfc, 0x77}; 337 | //bmsSerial.write(data, 7); 338 | sendCommand(data, sizeof(data)); 339 | //commSerial.println("Request info4 sent"); 340 | } 341 | 342 | void printBasicInfo() //debug all data to uart 343 | { 344 | TRACE; 345 | commSerial.printf("Total voltage: %f\n", (float)packBasicInfo.Volts / 1000); 346 | commSerial.printf("Amps: %f\n", (float)packBasicInfo.Amps / 1000); 347 | commSerial.printf("CapacityRemainAh: %f\n", (float)packBasicInfo.CapacityRemainAh / 1000); 348 | commSerial.printf("CapacityRemainPercent: %d\n", packBasicInfo.CapacityRemainPercent); 349 | commSerial.printf("Temp1: %f\n", (float)packBasicInfo.Temp1 / 10); 350 | commSerial.printf("Temp2: %f\n", (float)packBasicInfo.Temp2 / 10); 351 | commSerial.printf("Balance Code Low: 0x%x\n", packBasicInfo.BalanceCodeLow); 352 | commSerial.printf("Balance Code High: 0x%x\n", packBasicInfo.BalanceCodeHigh); 353 | commSerial.printf("Mosfet Status: 0x%x\n", packBasicInfo.MosfetStatus); 354 | } 355 | 356 | void printCellInfo() //debug all data to uart 357 | { 358 | TRACE; 359 | commSerial.printf("Number of cells: %u\n", packCellInfo.NumOfCells); 360 | for (byte i = 1; i <= packCellInfo.NumOfCells; i++) 361 | { 362 | commSerial.printf("Cell no. %u", i); 363 | commSerial.printf(" %f\n", (float)packCellInfo.CellVolt[i - 1] / 1000); 364 | } 365 | commSerial.printf("Max cell volt: %f\n", (float)packCellInfo.CellMax / 1000); 366 | commSerial.printf("Min cell volt: %f\n", (float)packCellInfo.CellMin / 1000); 367 | commSerial.printf("Difference cell volt: %f\n", (float)packCellInfo.CellDiff / 1000); 368 | commSerial.printf("Average cell volt: %f\n", (float)packCellInfo.CellAvg / 1000); 369 | commSerial.printf("Median cell volt: %f\n", (float)packCellInfo.CellMedian / 1000); 370 | commSerial.println(); 371 | } 372 | 373 | void hexDump(const char *data, uint32_t dataSize) //debug function 374 | { 375 | TRACE; 376 | commSerial.println("HEX data:"); 377 | 378 | for (int i = 0; i < dataSize; i++) 379 | { 380 | commSerial.printf("0x%x, ", data[i]); 381 | } 382 | commSerial.println(""); 383 | } 384 | 385 | int16_t two_ints_into16(int highbyte, int lowbyte) // turns two bytes into a single long integer 386 | { 387 | TRACE; 388 | int16_t result = (highbyte); 389 | result <<= 8; //Left shift 8 bits, 390 | result = (result | lowbyte); //OR operation, merge the two 391 | return result; 392 | } 393 | 394 | void constructBigString() //debug all data to uart 395 | { 396 | TRACE; 397 | stringBuffer[0] = '\0'; //clear old data 398 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Total voltage: %f\n", (float)packBasicInfo.Volts / 1000); 399 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Amps: %f\n", (float)packBasicInfo.Amps / 1000); 400 | snprintf(stringBuffer, STRINGBUFFERSIZE, "CapacityRemainAh: %f\n", (float)packBasicInfo.CapacityRemainAh / 1000); 401 | snprintf(stringBuffer, STRINGBUFFERSIZE, "CapacityRemainPercent: %d\n", packBasicInfo.CapacityRemainPercent); 402 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Temp1: %f\n", (float)packBasicInfo.Temp1 / 10); 403 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Temp2: %f\n", (float)packBasicInfo.Temp2 / 10); 404 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Balance Code Low: 0x%x\n", packBasicInfo.BalanceCodeLow); 405 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Balance Code High: 0x%x\n", packBasicInfo.BalanceCodeHigh); 406 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Mosfet Status: 0x%x\n", packBasicInfo.MosfetStatus); 407 | 408 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Number of cells: %u\n", packCellInfo.NumOfCells); 409 | for (byte i = 1; i <= packCellInfo.NumOfCells; i++) 410 | { 411 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Cell no. %u", i); 412 | snprintf(stringBuffer, STRINGBUFFERSIZE, " %f\n", (float)packCellInfo.CellVolt[i - 1] / 1000); 413 | } 414 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Max cell volt: %f\n", (float)packCellInfo.CellMax / 1000); 415 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Min cell volt: %f\n", (float)packCellInfo.CellMin / 1000); 416 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Difference cell volt: %f\n", (float)packCellInfo.CellDiff / 1000); 417 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Average cell volt: %f\n", (float)packCellInfo.CellAvg / 1000); 418 | snprintf(stringBuffer, STRINGBUFFERSIZE, "Median cell volt: %f\n", (float)packCellInfo.CellMedian / 1000); 419 | snprintf(stringBuffer, STRINGBUFFERSIZE, "\n"); 420 | } 421 | -------------------------------------------------------------------------------- /LCD.ino: -------------------------------------------------------------------------------- 1 | //lcd 128 x 160 px 2 | 3 | #define LCDCONSTRUCTOR TFT_eSPI 4 | LCDCONSTRUCTOR tft = TFT_eSPI(); 5 | 6 | #define SPRITECONSTRUCTOR TFT_eSprite 7 | SPRITECONSTRUCTOR bar = TFT_eSprite(&tft); 8 | 9 | void lcdclear() { 10 | tft.fillScreen(TFT_BLACK); // Clear screen 11 | } 12 | void lcdwarte() { 13 | delay(200); 14 | } 15 | void showInfoLcd() { 16 | 17 | TRACE; 18 | 19 | //tft.fillScreen(TFT_BLACK); // CLEAR makes nasty flicker, don't use 20 | tft.setCursor(0, 5); 21 | 22 | tft.setTextSize(2); 23 | 24 | //---main voltage--- 25 | 26 | tft.setTextColor(TFT_GREEN, TFT_BLACK); 27 | tft.print((float)packBasicInfo.Volts / 1000); 28 | 29 | tft.print("V"); 30 | tft.println(); 31 | 32 | /* 33 | //---single cell voltage--- 34 | tft.setTextColor(TFT_YELLOW, TFT_BLACK); 35 | tft.print("("); 36 | tft.print((float)packCellInfo.CellMedian / 1000); 37 | tft.print("V"); 38 | tft.print(")"); 39 | tft.println(); 40 | */ 41 | //---main current--- 42 | tft.setTextColor(TFT_GREENYELLOW, TFT_BLACK); 43 | tft.print((float)packBasicInfo.Amps / 1000); 44 | tft.print("A "); 45 | tft.println(); 46 | 47 | /* 48 | //---ampere hours--- 49 | tft.setTextColor(TFT_ORANGE, TFT_BLACK); 50 | tft.print((float)packBasicInfo.CapacityRemainAh / 1000); 51 | tft.print("Ah"); 52 | tft.println(); 53 | */ 54 | 55 | //---watts--- 56 | tft.setTextColor(TFT_ORANGE, TFT_BLACK); 57 | tft.print(packBasicInfo.Watts); 58 | tft.print("W "); 59 | tft.println(); 60 | 61 | //---battery percent--- 62 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 63 | tft.print(packBasicInfo.CapacityRemainPercent); 64 | tft.print("%"); 65 | //tft.println(); 66 | 67 | tft.print(" "); 68 | //---battery Wh--- 69 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 70 | tft.print(packBasicInfo.CapacityRemainWh); 71 | tft.print("Wh"); 72 | tft.println(); 73 | 74 | //---temperatures--- 75 | tft.setTextSize(1); 76 | tft.setTextColor(TFT_YELLOW, TFT_BLACK); 77 | tft.print((float)packBasicInfo.Temp1 / 10); 78 | tft.print("C"); 79 | tft.print(" "); 80 | tft.print((float)packBasicInfo.Temp2 / 10); 81 | tft.print("C"); 82 | tft.println(); 83 | 84 | //------------draw little battery symbols--------- 85 | bar.setColorDepth(18); 86 | bar.createSprite(128, 22); 87 | for (uint8_t i = 0; i < 12; i++) { 88 | // (uint8_t origin_x, uint8_t origin_y, uint8_t width, uint8_t height, float value, float valueMin, float valueMax, LCDCONSTRUCTOR &refLCD) 89 | lcdBargraphVertical(i * 10 + 4, 2, 8, 20, packCellInfo.CellVolt[i], c_cellAbsMin, c_cellAbsMax, packCellInfo.CellColor[i], packCellInfo.CellColorDisbalance[i], bar); 90 | } 91 | 92 | bar.pushSprite(0, 138); 93 | bar.deleteSprite(); 94 | 95 | //------------draw testing rectagle--------- 96 | /* 97 | tft.fillRect(120, 10, 5, 10, TFT_RED); 98 | tft.fillRect(120, 21, 5, 10, TFT_GREEN); 99 | tft.fillRect(120, 32, 5, 10, TFT_BLUE); 100 | //tft.fillRect(100, 10, 5, 10, color24to16(packCellInfo.CellColor[0])); 101 | */ 102 | 103 | //-------------print cell voltges-------- 104 | /* 105 | for (byte i = 1; i <= packCellInfo.NumOfCells; i++) 106 | { 107 | tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK); 108 | tft.print(i); 109 | if (i < 10) 110 | { 111 | tft.print(". "); 112 | } 113 | else 114 | { 115 | tft.print(". "); 116 | } 117 | tft.setTextColor(TFT_YELLOW, TFT_BLACK); 118 | tft.print((float)packCellInfo.CellVolt[i - 1] / 1000); 119 | tft.print("V"); 120 | if (i % 2 != 0) 121 | { 122 | tft.print(" "); 123 | } 124 | else 125 | { 126 | tft.println(); 127 | } 128 | } 129 | 130 | */ 131 | //----draw wifi icon---- 132 | // if (WiFi.status() == WL_CONNECTED) 133 | // { 134 | // tft.fillRect(122, 0, 5, 5, TFT_GREEN); 135 | // } 136 | // else 137 | // { 138 | // tft.fillRect(122, 0, 5, 5, TFT_DARKGREY); 139 | // } 140 | 141 | //-----draw progressbar----- 142 | 143 | //drawBar(3, 85, 122, 15, packBasicInfo.CapacityRemainPercent, TFT_WHITE, color24to16(getPixelColorHsv(mapHue(packBasicInfo.CapacityRemainPercent, 0, 100), 255, 255)), tft); 144 | 145 | //drawBar(3, 105, 122, 15, map(abs(packBasicInfo.Watts), 0, c_packMaxWatt, 0, 100), TFT_WHITE, color24to16(getPixelColorHsv(mapHue(abs(packBasicInfo.Watts), c_packMaxWatt, 0), 255, 255)), tft); 146 | 147 | char unit1[] = "%"; 148 | bar.setColorDepth(18); 149 | bar.createSprite(122, 20); 150 | drawBarUnit(0, 0, 122, 20, 0, 100, packBasicInfo.CapacityRemainPercent, TFT_WHITE, color24to16(getPixelColorHsv(mapHue(packBasicInfo.CapacityRemainPercent, 0, 100), 255, 255)), unit1, bar); 151 | bar.pushSprite(1, 80); 152 | bar.deleteSprite(); 153 | 154 | char unit2[] = "W"; 155 | bar.setColorDepth(18); 156 | bar.createSprite(122, 26); 157 | drawBarUnit(0, 0, 122, 26, 0, c_packMaxWatt, abs(packBasicInfo.Watts), TFT_WHITE, color24to16(getPixelColorHsv(mapHue(abs(packBasicInfo.Watts), c_packMaxWatt, 0), 255, 255)), unit2, bar); 158 | bar.pushSprite(1, 105); 159 | bar.deleteSprite(); 160 | } 161 | 162 | // void lcdStartNetworking() 163 | // { 164 | 165 | // tft.println("looking for wifi AP"); 166 | // } 167 | 168 | // void lcdNetworkStatus(uint8_t state) 169 | // { 170 | // switch (state) 171 | // { 172 | // case 0: 173 | // tft.println("wifi connected"); 174 | // tft.println(WiFi.localIP()); 175 | // break; 176 | // case 1: 177 | // tft.println("wifi failed connect"); 178 | // default: 179 | // tft.println("unkwon error"); 180 | // break; 181 | // } 182 | // } 183 | 184 | void lcdStartup() { 185 | tft.init(); 186 | tft.setTextWrap(false); 187 | tft.fillScreen(TFT_BLACK); // CLEAR 188 | 189 | tft.setRotation(0); // 190 | tft.setCursor(3, 5); 191 | tft.setTextColor(TFT_WHITE); 192 | tft.println("starting ebike"); 193 | } 194 | 195 | void lcdDisconnect() { 196 | tft.fillScreen(TFT_BLACK); // CLEAR 197 | tft.setCursor(3, 5); 198 | tft.setTextColor(TFT_RED); 199 | tft.setTextSize(2); 200 | tft.println("BMS lost"); 201 | 202 | tft.setTextColor(TFT_WHITE); 203 | tft.setTextSize(1); 204 | tft.println(""); 205 | tft.println("reconnecting...."); 206 | tft.println("(it may take a while)"); 207 | } 208 | 209 | void lcdConnected() { 210 | tft.setTextColor(TFT_GREEN); 211 | tft.println("connected!"); 212 | delay(200); 213 | tft.fillScreen(TFT_BLACK); // CLEAR 214 | } 215 | 216 | void lcdConnectionFailed() { 217 | 218 | commSerial.println("We have failed to connect to the server; there is nothin more we will do."); 219 | tft.setTextColor(TFT_RED); 220 | tft.println("connection failed!"); 221 | } 222 | 223 | void lcdConnectingStatus(uint8_t state) { 224 | switch (state) { 225 | case 0: 226 | tft.setTextColor(TFT_YELLOW); 227 | tft.setTextSize(1); 228 | tft.println("connecting to BMS..."); 229 | break; 230 | case 1: 231 | tft.println("created client"); 232 | break; 233 | case 2: 234 | tft.println("connected to server"); 235 | break; 236 | case 3: 237 | tft.println("service not found"); 238 | break; 239 | case 4: 240 | tft.println("found service"); 241 | break; 242 | case 5: 243 | tft.println("char. not found"); 244 | break; 245 | case 6: 246 | tft.println("char. found"); 247 | break; 248 | default: 249 | break; 250 | } 251 | } 252 | void lcdExample() { 253 | } 254 | 255 | void lcdBargraphVertical(uint8_t origin_x, uint8_t origin_y, uint8_t width, uint8_t height, uint16_t value, uint16_t valueMin, uint16_t valueMax, uint32_t insideColor, uint32_t outsideColor, SPRITECONSTRUCTOR &refLCD) { 256 | const uint8_t spacing = 2; 257 | if (value < valueMin) { 258 | value = valueMin; 259 | } 260 | if (value > valueMax) { 261 | value = valueMax; 262 | } 263 | 264 | uint8_t box_origin_x = origin_x + spacing; 265 | uint8_t box_origin_y = origin_y + spacing; 266 | uint8_t box_width = width - spacing * 2; 267 | uint8_t box_min_height = 1; 268 | uint8_t box_max_height = height - spacing; 269 | 270 | uint8_t nipple_origin_x = box_origin_x; 271 | uint8_t nipple_origin_y = origin_y - spacing; 272 | uint8_t nipple_width = box_width; 273 | uint8_t nipple_height = spacing; 274 | 275 | refLCD.fillRect(box_origin_x, box_origin_y, box_width, box_max_height, TFT_BLACK); // "delete" old battery 276 | 277 | int16_t box_height = map(value, valueMax, valueMin, box_min_height, box_max_height); 278 | box_origin_y = box_origin_y + box_height; 279 | box_height = box_max_height - box_height - spacing; 280 | if (box_height < 0) //ugly hack 281 | { 282 | box_height = 0; 283 | } 284 | outsideColor = 0xFAFAFA; 285 | refLCD.drawRect(origin_x, origin_y, width, height, color24to16(outsideColor)); //(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h) 286 | refLCD.fillRect(nipple_origin_x, nipple_origin_y, nipple_width, nipple_height, color24to16(outsideColor)); 287 | 288 | refLCD.fillRect(box_origin_x, box_origin_y, box_width, box_height, color24to16(insideColor)); 289 | } 290 | 291 | // void drawBar(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t percent, uint16_t frameColor, uint16_t barColor, LCDCONSTRUCTOR &refLCD) 292 | // { 293 | // if (true) //percent == 0) 294 | // { 295 | // refLCD.fillRoundRect(x, y, w, h, 3, TFT_BLACK); 296 | // } 297 | // uint8_t margin = 2; 298 | // uint16_t barHeight = h - 2 * margin; 299 | // uint16_t barWidth = w - 2 * margin; 300 | // refLCD.drawRoundRect(x, y, w, h, 3, frameColor); 301 | // refLCD.fillRect(x + margin, y + margin, barWidth * percent / 100.0, barHeight, barColor); 302 | // } 303 | 304 | void drawBar(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint32_t valueMin, uint32_t valueMax, uint32_t value, uint16_t frameColor, uint16_t barColor, SPRITECONSTRUCTOR &refLCD) { 305 | if (value < valueMin) { 306 | value = valueMin; 307 | } 308 | if (value > valueMax) { 309 | value = valueMax; 310 | } 311 | 312 | uint8_t margin = 2; 313 | uint16_t barHeight = h - 2 * margin; 314 | uint16_t barMaxWidth = w - 2 * margin; 315 | uint16_t barWidth = map(value, valueMin, valueMax, 0, barMaxWidth); //barMaxWidth * value / 100.0; 316 | 317 | refLCD.drawRoundRect(x, y, w, h, 3, frameColor); //frame 318 | 319 | refLCD.fillRect(x + margin, y + margin, barWidth, barHeight, barColor); // colored inside 320 | 321 | refLCD.fillRect(x + margin + barWidth, y + margin, barMaxWidth - barWidth, barHeight, TFT_BLACK); // inside bar, between actual value and max 322 | } 323 | 324 | void drawBarUnit(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint32_t valueMin, uint32_t valueMax, uint32_t value, uint16_t frameColor, uint16_t barColor, char *unit, SPRITECONSTRUCTOR &refLCD) { 325 | if (value < valueMin) { 326 | value = valueMin; 327 | } 328 | if (value > valueMax) { 329 | value = valueMax; 330 | } 331 | const uint16_t tw = 48; //half of text width 332 | const uint16_t th = 8; // half of text height 333 | const uint16_t border = 1; //black part of text 334 | const uint8_t font = 2; 335 | 336 | char tbuff[8]; 337 | 338 | sprintf(tbuff, "%4d", value); 339 | strcat(tbuff, unit); 340 | 341 | drawBar(x, y, w, h, valueMin, valueMax, value, frameColor, barColor, bar); //draw only bar 342 | //refLCD.setTextSize(3); 343 | refLCD.setTextColor(TFT_BLACK); 344 | refLCD.drawString(tbuff, x + w / 2 - border - tw, y + h / 2 + border - th, font); //black border, drawing same text offseted by 'border' all the way round 345 | refLCD.drawString(tbuff, x + w / 2 - border - tw, y + h / 2 - border - th, font); 346 | refLCD.drawString(tbuff, x + w / 2 + border - tw, y + h / 2 - border - th, font); 347 | refLCD.drawString(tbuff, x + w / 2 + border - tw, y + h / 2 - border - th, font); 348 | refLCD.drawString(tbuff, x + w / 2 + border - tw, y + h / 2 + border - th, font); 349 | refLCD.drawString(tbuff, x + w / 2 + border - tw, y + h / 2 + border - th, font); 350 | refLCD.drawString(tbuff, x + w / 2 + border - tw, y + h / 2 + border - th, font); 351 | refLCD.drawString(tbuff, x + w / 2 - border - tw, y + h / 2 + border - th, font); 352 | 353 | refLCD.setTextColor(TFT_WHITE); 354 | refLCD.drawString(tbuff, x + w / 2 - tw, y + h / 2 - th, font); //finally, the white text 355 | } 356 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Announcement 2 | 3 | This project is no longer actively maintained. I will not be able to provide support for any issues, bugs or feature requests. However, I will still be accepting pull requests. If you wish to contribute to the project, you're welcome to fork the repository and make any changes you see fit. 4 | 5 | For users looking to communicate with BMS, a good starting point would be [this project](https://github.com/syssi/esphome-jbd-bms). 6 | 7 | 8 | # Smart-BMS-Bluetooth-ESP32 9 | Program to read out and display data from xiaoxiang Smart BMS over Bluetooth Low Energy 10 | https://www.lithiumbatterypcb.com/ 11 | Tested with original BLE module provided. Might work with generic BLE module when UUIDs are modified 12 | 13 | Needs ESP32 and graphic display. 14 | Tested on TTGO TS https://github.com/LilyGO/TTGO-TS 15 | 16 | (c) Miroslav Kolinsky 2019 https://www.kolins.cz 17 | 18 | thanks to Petr Jenik for big parts of code 19 | thanks to Milan Petrzilka 20 | 21 | heavily inspired by https://github.com/bres55/Smart-BMS-arduino-Reader 22 | 23 | known bugs: 24 | * if BLE server is not available during startup, program hangs 25 | * reconnection sort of works, sometimes ESP reboots 26 | * GUI needs to be done 27 | 28 | Work in progress... 29 | 30 | ![Image](1.jpg) 31 | ![Image](2.jpg) 32 | -------------------------------------------------------------------------------- /RGB-LED.ino: -------------------------------------------------------------------------------- 1 | /* 2 | const uint16_t PixelCount = 12; // this example assumes 4 pixels, making it smaller will cause a failure 3 | const uint8_t PixelPin = 26; // make sure to set this to the correct pin, ignored for Esp8266 4 | NeoPixelBrightnessBus strip(PixelCount, PixelPin); 5 | 6 | void stripStartup() 7 | { 8 | strip.Begin(); 9 | strip.Show(); // Initialize all pixels to 'off' 10 | strip.SetBrightness(64); 11 | } 12 | 13 | uint16_t mySpectrum = 0; 14 | void stripTest() 15 | { 16 | for (uint16_t i = 0; i < 12; i++) // fill up all 12 leds 17 | { 18 | strip.SetPixelColor(i, HtmlColor(packCellInfo.CellColor[i])); 19 | } 20 | strip.Show(); 21 | } 22 | */ -------------------------------------------------------------------------------- /Smart-BMS-Bluetooth-ESP32.ino: -------------------------------------------------------------------------------- 1 | /** 2 | Program to read out and display 3 | data from xiaoxiang Smart BMS 4 | over Bluetooth Low Energy 5 | https://www.lithiumbatterypcb.com/ 6 | Tested with original BLE module provided. 7 | Might work with generic BLE module when UUIDs are modified 8 | 9 | Needs ESP32 and graphic display. 10 | Tested on TTGO TS https://github.com/LilyGO/TTGO-TS 11 | 12 | (c) Miroslav Kolinsky 2019 13 | https://www.kolins.cz 14 | 15 | thanks to Petr Jenik for big parts of code 16 | thanks to Milan Petrzilka 17 | 18 | known bugs: 19 | -if BLE server is not available during startup, program hangs 20 | -reconnection sort of works, sometimes ESP reboots 21 | */ 22 | #define eSPI 1 23 | //#define oled 1 24 | //#define TRACE commSerial.println(__FUNCTION__) 25 | #define TRACE 26 | #include 27 | #include "BLEDevice.h" 28 | #include "BLEScan.h" //why this is commented? 29 | #include "mydatatypes.h" 30 | #include 31 | #include "Adafruit_GFX.h" 32 | #include "Adafruit_ILI9341.h" 33 | #ifdef eSPI 34 | #include "TFT_eSPI.h" 35 | #endif 36 | #include 37 | #include 38 | #include 39 | #include "settings.h" 40 | 41 | // #include 42 | // #include 43 | // #include 44 | // #include 45 | // #include "webpages.h" 46 | 47 | HardwareSerial commSerial(0); 48 | HardwareSerial bmsSerial(1); 49 | //WebServer server(80); 50 | 51 | //---- global variables ---- 52 | 53 | static boolean doConnect = false; 54 | static boolean BLE_client_connected = false; 55 | static boolean doScan = false; 56 | 57 | packBasicInfoStruct packBasicInfo; //here shall be the latest data got from BMS 58 | packEepromStruct packEeprom; //here shall be the latest data got from BMS 59 | packCellInfoStruct packCellInfo; //here shall be the latest data got from BMS 60 | 61 | const byte cBasicInfo3 = 3; //type of packet 3= basic info 62 | const byte cCellInfo4 = 4; //type of packet 4= individual cell info 63 | 64 | unsigned long previousMillis = 0; 65 | unsigned long previousMillisl = 0; 66 | const long myinterval = 5000; 67 | const long interval = 4000; 68 | 69 | bool toggle = false; 70 | bool newPacketReceived = false; 71 | int i = 0; 72 | void setup() { 73 | 74 | commSerial.begin(115200, SERIAL_8N1, 3, 1); 75 | bmsSerial.begin(9600, SERIAL_8N1, 21, 22); 76 | commSerial.println("Starting BMS dashboard application..."); 77 | // stripStartup(); 78 | // oled_startup(); 79 | lcdStartup(); 80 | // newtworkStartup(); 81 | commSerial.println("LCD fertig..."); 82 | 83 | bleStartup(); 84 | lcdclear(); 85 | showInfoLcd(); 86 | lcdwarte(); 87 | // linearMeter(99, 10, 310, 5, 10, 1, 30, 5); 88 | // for (int wi = 0; wi <= 100; wi = wi + 5) { 89 | // kreis(wi,120, 90,80,80); 90 | // delay(500); 91 | // } 92 | } 93 | //---------------------main loop------------------ 94 | void loop() { 95 | //server.handleClient(); 96 | bleRequestData(); 97 | 98 | /* 99 | if (newPacketReceived == true) 100 | { 101 | showInfoLcd(); 102 | printBasicInfo(); 103 | printCellInfo(); 104 | commSerial.println("-"); 105 | 106 | } 107 | commSerial.print("."); 108 | i++; 109 | if (i > 60) { 110 | i = 0; 111 | commSerial.println(); 112 | } 113 | */ 114 | delay(1000); 115 | } 116 | //---------------------/ main loop------------------ 117 | -------------------------------------------------------------------------------- /User_Setup-arduitouch.h: -------------------------------------------------------------------------------- 1 | //user setup arduitouch 2 | #define ILI9341_DRIVER // Generic driver for common displays 3 | //#define ST7735_DRIVER // Define additional parameters below for this display 4 | #define TFT_BL 15 // LED back-light control pin 5 | #define TFT_BACKLIGHT_ON LOW // Level to turn ON back-light (HIGH or LOW) 6 | #define TFT_BACKLIGHT_OFF HIGH // Level to turn ON back-light (HIGH or LOW) 7 | // For ESP32 Dev board (only tested with ILI9341 display) 8 | // The hardware SPI can be mapped to any pins 9 | 10 | #define TFT_MISO 19 11 | #define TFT_MOSI 23 12 | #define TFT_SCLK 18 13 | #define TFT_CS 5 // Chip select control pin 14 | #define TFT_DC 4 // Data Command control pin 15 | #define TFT_RST 22 // Reset pin (could connect to RST pin) 16 | 17 | #define TOUCH_CS 14 // Chip select pin (T_CS) of touch screen 18 | 19 | 20 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 21 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 22 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 23 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 24 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 25 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 26 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 27 | #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 28 | 29 | // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded 30 | // this will save ~20kbytes of FLASH 31 | #define SMOOTH_FONT 32 | #define SPI_FREQUENCY 27000000 33 | // #define SPI_FREQUENCY 40000000 34 | // #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) 35 | // #define SPI_FREQUENCY 80000000 36 | 37 | // Optional reduced SPI frequency for reading TFT 38 | #define SPI_READ_FREQUENCY 20000000 39 | 40 | // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: 41 | #define SPI_TOUCH_FREQUENCY 2500000 42 | -------------------------------------------------------------------------------- /User_Setup-bigger_display_ttgo.h: -------------------------------------------------------------------------------- 1 | // USER DEFINED SETTINGS 2 | // Set driver type, fonts to be loaded, pins used and SPI control method etc 3 | // 4 | // See the User_Setup_Select.h file if you wish to be able to define multiple 5 | // setups and then easily select which setup file is used by the compiler. 6 | // 7 | // If this file is edited correctly then all the library example sketches should 8 | // run without the need to make any more changes for a particular hardware setup! 9 | // Note that some sketches are designed for a particular TFT pixel width/height 10 | 11 | 12 | // ################################################################################## 13 | // 14 | // Section 1. Call up the right driver file and any options for it 15 | // 16 | // ################################################################################## 17 | 18 | // Only define one driver, the other ones must be commented out 19 | //#define ILI9341_DRIVER 20 | #define ST7735_DRIVER // Define additional parameters below for this display 21 | //#define ILI9163_DRIVER // Define additional parameters below for this display 22 | //#define S6D02A1_DRIVER 23 | //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI 24 | //#define HX8357D_DRIVER 25 | //#define ILI9481_DRIVER 26 | //#define ILI9486_DRIVER 27 | //#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) 28 | //#define ST7789_DRIVER // Define additional parameters below for this display 29 | //#define R61581_DRIVER 30 | 31 | // Some displays support SPI reads via the MISO pin, other displays have a single 32 | // bi-directional SDA pin and the library will try to read this via the MOSI line. 33 | // To use the SDA line for reading data from the TFT uncomment the following line: 34 | 35 | // #define TFT_SDA_READ // This option if for ESP32 ONLY, tested with ST7789 display only 36 | 37 | // For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display 38 | // Try ONE option at a time to find the correct colour order for your display 39 | 40 | //#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue 41 | //#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red 42 | 43 | // For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below 44 | 45 | // #define M5STACK 46 | 47 | // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation 48 | // #define TFT_WIDTH 80 49 | #define TFT_WIDTH 128 50 | // #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 51 | #define TFT_HEIGHT 160 52 | // #define TFT_HEIGHT 128 53 | // #define TFT_HEIGHT 240 // ST7789 240 x 240 54 | // #define TFT_HEIGHT 320 // ST7789 240 x 320 55 | 56 | // For ST7735 ONLY, define the type of display, originally this was based on the 57 | // colour of the tab on the screen protector film but this is not always true, so try 58 | // out the different options below if the screen does not display graphics correctly, 59 | // e.g. colours wrong, mirror images, or tray pixels at the edges. 60 | // Comment out ALL BUT ONE of these options for a ST7735 display driver, save this 61 | // this User_Setup file, then rebuild and upload the sketch to the board again: 62 | 63 | // #define ST7735_INITB 64 | //#define ST7735_GREENTAB 65 | #define ST7735_GREENTAB2 66 | // #define ST7735_GREENTAB3 67 | // #define ST7735_GREENTAB128 // For 128 x 128 display 68 | // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) 69 | // #define ST7735_REDTAB 70 | // #define ST7735_BLACKTAB 71 | // #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset 72 | 73 | // If colours are inverted (white shows as black) then uncomment one of the next 74 | // 2 lines try both options, one of the options should correct the inversion. 75 | 76 | // #define TFT_INVERSION_ON 77 | // #define TFT_INVERSION_OFF 78 | 79 | // If a backlight control signal is available then define the TFT_BL pin in Section 2 80 | // below. The backlight will be turned ON when tft.begin() is called, but the library 81 | // needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be 82 | // driven with a PWM signal or turned OFF/ON then this must be handled by the user 83 | // sketch. e.g. with digitalWrite(TFT_BL, LOW); 84 | 85 | // #define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options 86 | 87 | // ################################################################################## 88 | // 89 | // Section 2. Define the pins that are used to interface with the display here 90 | // 91 | // ################################################################################## 92 | 93 | // We must use hardware SPI, a minimum of 3 GPIO pins is needed. 94 | // Typical setup for ESP8266 NodeMCU ESP-12 is : 95 | // 96 | // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) 97 | // Display LED to NodeMCU pin VIN (or 5V, see below) 98 | // Display SCK to NodeMCU pin D5 99 | // Display SDI/MOSI to NodeMCU pin D7 100 | // Display DC (RS/AO)to NodeMCU pin D3 101 | // Display RESET to NodeMCU pin D4 (or RST, see below) 102 | // Display CS to NodeMCU pin D8 (or GND, see below) 103 | // Display GND to NodeMCU pin GND (0V) 104 | // Display VCC to NodeMCU 5V or 3.3V 105 | // 106 | // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin 107 | // 108 | // The DC (Data Command) pin may be labeled AO or RS (Register Select) 109 | // 110 | // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more 111 | // SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS 112 | // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin 113 | // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. 114 | // 115 | // The NodeMCU D0 pin can be used for RST 116 | // 117 | // 118 | // Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin 119 | // If 5V is not available at a pin you can use 3.3V but backlight brightness 120 | // will be lower. 121 | 122 | 123 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### 124 | 125 | // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation 126 | #define TFT_CS PIN_D8 // Chip select control pin D8 127 | #define TFT_DC PIN_D3 // Data Command control pin 128 | #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 129 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 130 | 131 | //#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) 132 | 133 | //#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen 134 | 135 | //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only 136 | 137 | 138 | // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### 139 | 140 | // Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact 141 | // but saves pins for other functions. 142 | // Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode 143 | 144 | // In ESP8266 overlap mode the following must be defined 145 | //#define TFT_SPI_OVERLAP 146 | 147 | // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 148 | //#define TFT_CS PIN_D3 149 | //#define TFT_DC PIN_D5 // Data Command control pin 150 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 151 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 152 | 153 | 154 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### 155 | 156 | // For ESP32 Dev board (only tested with ILI9341 display) 157 | // The hardware SPI can be mapped to any pins 158 | 159 | #define TFT_MISO -1 160 | #define TFT_MOSI 23 161 | #define TFT_SCLK 5 162 | #define TFT_CS 16 // Chip select control pin 163 | #define TFT_DC 17 // Data Command control pin 164 | //#define TFT_RST 4 // Reset pin (could connect to RST pin) 165 | #define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST 166 | 167 | //#define TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) 168 | 169 | //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen 170 | 171 | //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only 172 | 173 | // For the M5Stack module use these #define lines 174 | //#define TFT_MISO 19 175 | //#define TFT_MOSI 23 176 | //#define TFT_SCLK 18 177 | //#define TFT_CS 14 // Chip select control pin 178 | //#define TFT_DC 27 // Data Command control pin 179 | //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) 180 | //#define TFT_BL 32 // LED back-light (required for M5Stack) 181 | 182 | // ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### 183 | 184 | // The library supports 8 bit parallel TFTs with the ESP32, the pin 185 | // selection below is compatible with ESP32 boards in UNO format. 186 | // Wemos D32 boards need to be modified, see diagram in Tools folder. 187 | // Only ILI9481 and ILI9341 based displays have been tested! 188 | 189 | // Parallel bus is only supported on ESP32 190 | // Uncomment line below to use ESP32 Parallel interface instead of SPI 191 | 192 | //#define ESP32_PARALLEL 193 | 194 | // The ESP32 and TFT the pins used for testing are: 195 | //#define TFT_CS 33 // Chip select control pin (library pulls permanently low 196 | //#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 197 | //#define TFT_RST 32 // Reset pin, toggles on startup 198 | 199 | //#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 200 | //#define TFT_RD 2 // Read strobe control pin 201 | 202 | //#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus 203 | //#define TFT_D1 13 // so a single register write sets/clears all bits. 204 | //#define TFT_D2 26 // Pins can be randomly assigned, this does not affect 205 | //#define TFT_D3 25 // TFT screen update performance. 206 | //#define TFT_D4 17 207 | //#define TFT_D5 16 208 | //#define TFT_D6 27 209 | //#define TFT_D7 14 210 | 211 | 212 | // ################################################################################## 213 | // 214 | // Section 3. Define the fonts that are to be used here 215 | // 216 | // ################################################################################## 217 | 218 | // Comment out the #defines below with // to stop that font being loaded 219 | // The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not 220 | // normally necessary. If all fonts are loaded the extra FLASH space required is 221 | // about 17Kbytes. To save FLASH space only enable the fonts you need! 222 | 223 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 224 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 225 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 226 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 227 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 228 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 229 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 230 | //#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 231 | 232 | // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded 233 | // this will save ~20kbytes of FLASH 234 | //#define SMOOTH_FONT 235 | 236 | 237 | // ################################################################################## 238 | // 239 | // Section 4. Other options 240 | // 241 | // ################################################################################## 242 | 243 | // Define the SPI clock frequency, this affects the graphics rendering speed. Too 244 | // fast and the TFT driver will not keep up and display corruption appears. 245 | // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails 246 | // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) 247 | // With an ILI9163 display 27 MHz works OK. 248 | // The RPi typically only works at 20MHz maximum. 249 | 250 | // #define SPI_FREQUENCY 1000000 251 | // #define SPI_FREQUENCY 5000000 252 | // #define SPI_FREQUENCY 10000000 253 | // #define SPI_FREQUENCY 20000000 254 | #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 255 | // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS 256 | // #define SPI_FREQUENCY 80000000 257 | 258 | // Optional reduced SPI frequency for reading TFT 259 | #define SPI_READ_FREQUENCY 20000000 260 | 261 | // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: 262 | #define SPI_TOUCH_FREQUENCY 2500000 263 | 264 | // The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. 265 | // If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) 266 | // then uncomment the following line: 267 | //#define USE_HSPI_PORT 268 | 269 | // Comment out the following #define if "SPI Transactions" do not need to be 270 | // supported. When commented out the code size will be smaller and sketches will 271 | // run slightly faster, so leave it commented out unless you need it! 272 | 273 | // Transaction support is needed to work with SD library but not needed with TFT_SdFat 274 | // Transaction support is required if other SPI devices are connected. 275 | 276 | // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) 277 | // so changing it here has no effect 278 | 279 | // #define SUPPORT_TRANSACTIONS 280 | -------------------------------------------------------------------------------- /User_Setup-smaller_display_ttgo.h: -------------------------------------------------------------------------------- 1 | // USER DEFINED SETTINGS 2 | // Set driver type, fonts to be loaded, pins used and SPI control method etc 3 | // 4 | // See the User_Setup_Select.h file if you wish to be able to define multiple 5 | // setups and then easily select which setup file is used by the compiler. 6 | // 7 | // If this file is edited correctly then all the library example sketches should 8 | // run without the need to make any more changes for a particular hardware setup! 9 | // Note that some sketches are designed for a particular TFT pixel width/height 10 | 11 | 12 | // ################################################################################## 13 | // 14 | // Section 1. Call up the right driver file and any options for it 15 | // 16 | // ################################################################################## 17 | 18 | // Only define one driver, the other ones must be commented out 19 | //#define ILI9341_DRIVER 20 | #define ST7735_DRIVER // Define additional parameters below for this display 21 | //#define ILI9163_DRIVER // Define additional parameters below for this display 22 | //#define S6D02A1_DRIVER 23 | //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI 24 | //#define HX8357D_DRIVER 25 | //#define ILI9481_DRIVER 26 | //#define ILI9486_DRIVER 27 | //#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) 28 | //#define ST7789_DRIVER // Define additional parameters below for this display 29 | //#define R61581_DRIVER 30 | 31 | // Some displays support SPI reads via the MISO pin, other displays have a single 32 | // bi-directional SDA pin and the library will try to read this via the MOSI line. 33 | // To use the SDA line for reading data from the TFT uncomment the following line: 34 | 35 | // #define TFT_SDA_READ // This option if for ESP32 ONLY, tested with ST7789 display only 36 | 37 | // For ST7789 ONLY, define the colour order IF the blue and red are swapped on your display 38 | // Try ONE option at a time to find the correct colour order for your display 39 | 40 | // #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue 41 | #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red 42 | 43 | // For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below 44 | 45 | // #define M5STACK 46 | 47 | // For ST7789, ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation 48 | // #define TFT_WIDTH 80 49 | #define TFT_WIDTH 128 50 | // #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 51 | #define TFT_HEIGHT 160 52 | // #define TFT_HEIGHT 128 53 | // #define TFT_HEIGHT 240 // ST7789 240 x 240 54 | // #define TFT_HEIGHT 320 // ST7789 240 x 320 55 | 56 | // For ST7735 ONLY, define the type of display, originally this was based on the 57 | // colour of the tab on the screen protector film but this is not always true, so try 58 | // out the different options below if the screen does not display graphics correctly, 59 | // e.g. colours wrong, mirror images, or tray pixels at the edges. 60 | // Comment out ALL BUT ONE of these options for a ST7735 display driver, save this 61 | // this User_Setup file, then rebuild and upload the sketch to the board again: 62 | 63 | // #define ST7735_INITB 64 | #define ST7735_GREENTAB 65 | // #define ST7735_GREENTAB2 66 | // #define ST7735_GREENTAB3 67 | // #define ST7735_GREENTAB128 // For 128 x 128 display 68 | // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) 69 | // #define ST7735_REDTAB 70 | // #define ST7735_BLACKTAB 71 | // #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset 72 | 73 | // If colours are inverted (white shows as black) then uncomment one of the next 74 | // 2 lines try both options, one of the options should correct the inversion. 75 | 76 | // #define TFT_INVERSION_ON 77 | // #define TFT_INVERSION_OFF 78 | 79 | // If a backlight control signal is available then define the TFT_BL pin in Section 2 80 | // below. The backlight will be turned ON when tft.begin() is called, but the library 81 | // needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be 82 | // driven with a PWM signal or turned OFF/ON then this must be handled by the user 83 | // sketch. e.g. with digitalWrite(TFT_BL, LOW); 84 | 85 | // #define TFT_BACKLIGHT_ON HIGH // HIGH or LOW are options 86 | 87 | // ################################################################################## 88 | // 89 | // Section 2. Define the pins that are used to interface with the display here 90 | // 91 | // ################################################################################## 92 | 93 | // We must use hardware SPI, a minimum of 3 GPIO pins is needed. 94 | // Typical setup for ESP8266 NodeMCU ESP-12 is : 95 | // 96 | // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) 97 | // Display LED to NodeMCU pin VIN (or 5V, see below) 98 | // Display SCK to NodeMCU pin D5 99 | // Display SDI/MOSI to NodeMCU pin D7 100 | // Display DC (RS/AO)to NodeMCU pin D3 101 | // Display RESET to NodeMCU pin D4 (or RST, see below) 102 | // Display CS to NodeMCU pin D8 (or GND, see below) 103 | // Display GND to NodeMCU pin GND (0V) 104 | // Display VCC to NodeMCU 5V or 3.3V 105 | // 106 | // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin 107 | // 108 | // The DC (Data Command) pin may be labeled AO or RS (Register Select) 109 | // 110 | // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more 111 | // SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS 112 | // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin 113 | // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. 114 | // 115 | // The NodeMCU D0 pin can be used for RST 116 | // 117 | // 118 | // Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin 119 | // If 5V is not available at a pin you can use 3.3V but backlight brightness 120 | // will be lower. 121 | 122 | 123 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### 124 | 125 | // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation 126 | #define TFT_CS PIN_D8 // Chip select control pin D8 127 | #define TFT_DC PIN_D3 // Data Command control pin 128 | #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 129 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 130 | 131 | //#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) 132 | 133 | //#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen 134 | 135 | //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only 136 | 137 | 138 | // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### 139 | 140 | // Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact 141 | // but saves pins for other functions. 142 | // Use NodeMCU SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode 143 | 144 | // In ESP8266 overlap mode the following must be defined 145 | //#define TFT_SPI_OVERLAP 146 | 147 | // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 148 | //#define TFT_CS PIN_D3 149 | //#define TFT_DC PIN_D5 // Data Command control pin 150 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 151 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 152 | 153 | 154 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### 155 | 156 | // For ESP32 Dev board (only tested with ILI9341 display) 157 | // The hardware SPI can be mapped to any pins 158 | 159 | #define TFT_MISO -1 160 | #define TFT_MOSI 23 161 | #define TFT_SCLK 5 162 | #define TFT_CS 16 // Chip select control pin 163 | #define TFT_DC 17 // Data Command control pin 164 | //#define TFT_RST 4 // Reset pin (could connect to RST pin) 165 | #define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST 166 | 167 | //#define TFT_BL 32 // LED back-light (only for ST7789 with backlight control pin) 168 | 169 | //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen 170 | 171 | //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only 172 | 173 | // For the M5Stack module use these #define lines 174 | //#define TFT_MISO 19 175 | //#define TFT_MOSI 23 176 | //#define TFT_SCLK 18 177 | //#define TFT_CS 14 // Chip select control pin 178 | //#define TFT_DC 27 // Data Command control pin 179 | //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) 180 | //#define TFT_BL 32 // LED back-light (required for M5Stack) 181 | 182 | // ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### 183 | 184 | // The library supports 8 bit parallel TFTs with the ESP32, the pin 185 | // selection below is compatible with ESP32 boards in UNO format. 186 | // Wemos D32 boards need to be modified, see diagram in Tools folder. 187 | // Only ILI9481 and ILI9341 based displays have been tested! 188 | 189 | // Parallel bus is only supported on ESP32 190 | // Uncomment line below to use ESP32 Parallel interface instead of SPI 191 | 192 | //#define ESP32_PARALLEL 193 | 194 | // The ESP32 and TFT the pins used for testing are: 195 | //#define TFT_CS 33 // Chip select control pin (library pulls permanently low 196 | //#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 197 | //#define TFT_RST 32 // Reset pin, toggles on startup 198 | 199 | //#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 200 | //#define TFT_RD 2 // Read strobe control pin 201 | 202 | //#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus 203 | //#define TFT_D1 13 // so a single register write sets/clears all bits. 204 | //#define TFT_D2 26 // Pins can be randomly assigned, this does not affect 205 | //#define TFT_D3 25 // TFT screen update performance. 206 | //#define TFT_D4 17 207 | //#define TFT_D5 16 208 | //#define TFT_D6 27 209 | //#define TFT_D7 14 210 | 211 | 212 | // ################################################################################## 213 | // 214 | // Section 3. Define the fonts that are to be used here 215 | // 216 | // ################################################################################## 217 | 218 | // Comment out the #defines below with // to stop that font being loaded 219 | // The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not 220 | // normally necessary. If all fonts are loaded the extra FLASH space required is 221 | // about 17Kbytes. To save FLASH space only enable the fonts you need! 222 | 223 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 224 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 225 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 226 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 227 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 228 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 229 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 230 | //#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 231 | 232 | // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded 233 | // this will save ~20kbytes of FLASH 234 | //#define SMOOTH_FONT 235 | 236 | 237 | // ################################################################################## 238 | // 239 | // Section 4. Other options 240 | // 241 | // ################################################################################## 242 | 243 | // Define the SPI clock frequency, this affects the graphics rendering speed. Too 244 | // fast and the TFT driver will not keep up and display corruption appears. 245 | // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails 246 | // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) 247 | // With an ILI9163 display 27 MHz works OK. 248 | // The RPi typically only works at 20MHz maximum. 249 | 250 | // #define SPI_FREQUENCY 1000000 251 | // #define SPI_FREQUENCY 5000000 252 | // #define SPI_FREQUENCY 10000000 253 | // #define SPI_FREQUENCY 20000000 254 | #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 255 | // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS 256 | // #define SPI_FREQUENCY 80000000 257 | 258 | // Optional reduced SPI frequency for reading TFT 259 | #define SPI_READ_FREQUENCY 20000000 260 | 261 | // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: 262 | #define SPI_TOUCH_FREQUENCY 2500000 263 | 264 | // The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. 265 | // If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) 266 | // then uncomment the following line: 267 | //#define USE_HSPI_PORT 268 | 269 | // Comment out the following #define if "SPI Transactions" do not need to be 270 | // supported. When commented out the code size will be smaller and sketches will 271 | // run slightly faster, so leave it commented out unless you need it! 272 | 273 | // Transaction support is needed to work with SD library but not needed with TFT_SdFat 274 | // Transaction support is required if other SPI devices are connected. 275 | 276 | // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) 277 | // so changing it here has no effect 278 | 279 | // #define SUPPORT_TRANSACTIONS 280 | -------------------------------------------------------------------------------- /User_Setup-t-beam.h: -------------------------------------------------------------------------------- 1 | // USER DEFINED SETTINGS geändert für T-Beam 2 | 3 | // es darf nur ein Treiber definiert werden, die anderen müssen auskommentiert sein. 4 | #define ILI9341_DRIVER // Generic driver for common displays 5 | //#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172 6 | 7 | 8 | // ################################################################################## 9 | // 10 | // Section 2. Define the pins that are used to interface with the display here 11 | // 12 | // ################################################################################## 13 | 14 | // If a backlight control signal is available then define the TFT_BL pin in Section 2 15 | // below. The backlight will be turned ON when tft.begin() is called, but the library 16 | // needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be 17 | // driven with a PWM signal or turned OFF/ON then this must be handled by the user 18 | // sketch. e.g. with digitalWrite(TFT_BL, LOW); 19 | 20 | #define TFT_BL 4 // LED back-light control pin 21 | #define TFT_BACKLIGHT_ON HIGH // LOW //Level to turn ON back-light (HIGH or LOW) 22 | #define TFT_BACKLIGHT_OFF LOW // HIGH //Level to turn ON back-light (HIGH or LOW) 23 | 24 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### 25 | 26 | // For ESP32 Dev board (only tested with ILI9341 display) 27 | // The hardware SPI can be mapped to any pins 28 | 29 | #define TFT_MISO 25 30 | #define TFT_MOSI 13 31 | #define TFT_SCLK 15 32 | #define TFT_CS 0 // Chip select control pin 33 | #define TFT_DC 14 // Data Command control pin 34 | //#define TFT_RST 2 // Reset pin (could connect to RST pin) 35 | #define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST 36 | 37 | 38 | #define TOUCH_CS 2 // Chip select pin (T_CS) of touch screen 39 | 40 | 41 | // ################################################################################## 42 | // 43 | // Section 3. Define the fonts that are to be used here 44 | // 45 | // ################################################################################## 46 | 47 | // Comment out the #defines below with // to stop that font being loaded 48 | // The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not 49 | // normally necessary. If all fonts are loaded the extra FLASH space required is 50 | // about 17Kbytes. To save FLASH space only enable the fonts you need! 51 | 52 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 53 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 54 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 55 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 56 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 57 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 58 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 59 | #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 60 | 61 | // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded 62 | // this will save ~20kbytes of FLASH 63 | #define SMOOTH_FONT 64 | 65 | 66 | // ################################################################################## 67 | // 68 | // Section 4. Other options 69 | // 70 | // ################################################################################## 71 | 72 | // Define the SPI clock frequency, this affects the graphics rendering speed. Too 73 | // fast and the TFT driver will not keep up and display corruption appears. 74 | // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails 75 | // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) 76 | // With an ILI9163 display 27 MHz works OK. 77 | 78 | // #define SPI_FREQUENCY 1000000 79 | // #define SPI_FREQUENCY 5000000 80 | // #define SPI_FREQUENCY 10000000 81 | // #define SPI_FREQUENCY 20000000 82 | #define SPI_FREQUENCY 27000000 //default 83 | // #define SPI_FREQUENCY 40000000 84 | // #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) 85 | // #define SPI_FREQUENCY 80000000 86 | 87 | // Optional reduced SPI frequency for reading TFT 88 | #define SPI_READ_FREQUENCY 20000000 89 | 90 | // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: 91 | #define SPI_TOUCH_FREQUENCY 2500000 92 | 93 | // The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. 94 | // If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) 95 | // then uncomment the following line: 96 | #define USE_HSPI_PORT 97 | 98 | // Comment out the following #define if "SPI Transactions" do not need to be 99 | // supported. When commented out the code size will be smaller and sketches will 100 | // run slightly faster, so leave it commented out unless you need it! 101 | 102 | // Transaction support is needed to work with SD library but not needed with TFT_SdFat 103 | // Transaction support is required if other SPI devices are connected. 104 | 105 | // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) 106 | // so changing it here has no effect 107 | 108 | // #define SUPPORT_TRANSACTIONS 109 | -------------------------------------------------------------------------------- /color_magic.ino: -------------------------------------------------------------------------------- 1 | unsigned int color24to16(unsigned long rgb) 2 | { //convert 24 bit RGB to 16bit 5:6:5 RGB 3 | 4 | return (((rgb & 0xf80000) >> 8) | ((rgb & 0xfc00) >> 5) | ((rgb & 0xf8) >> 3)); 5 | } 6 | 7 | 8 | uint16_t mapHue(uint16_t input, uint16_t min, uint16_t max) 9 | {//maps hue from 0 to max to 330 to 120 like in betaflight. hue can be 0-359 hence the condidion used 10 | uint16_t temp = map(input, min, max, 1, 150); 11 | 12 | if (temp < 30) 13 | { 14 | return 330 + temp; 15 | } 16 | else 17 | { 18 | return temp - 30; 19 | } 20 | } 21 | 22 | 23 | 24 | const byte dim_curve[] = { 25 | 0, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 26 | 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 27 | 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 28 | 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 29 | 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 30 | 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 31 | 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 32 | 20, 20, 21, 21, 22, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26, 26, 33 | 27, 27, 28, 28, 29, 29, 30, 30, 31, 32, 32, 33, 33, 34, 35, 35, 34 | 36, 36, 37, 38, 38, 39, 40, 40, 41, 42, 43, 43, 44, 45, 46, 47, 35 | 48, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 36 | 63, 64, 65, 66, 68, 69, 70, 71, 73, 74, 75, 76, 78, 79, 81, 82, 37 | 83, 85, 86, 88, 90, 91, 93, 94, 96, 98, 99, 101, 103, 105, 107, 109, 38 | 110, 112, 114, 116, 118, 121, 123, 125, 127, 129, 132, 134, 136, 139, 141, 144, 39 | 146, 149, 151, 154, 157, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 190, 40 | 193, 196, 200, 203, 207, 211, 214, 218, 222, 226, 230, 234, 238, 242, 248, 255, 41 | }; 42 | 43 | // downloaded from here 44 | // https://www.kasperkamperman.com/blog/arduino/arduino-programming-hsb-to-rgb/ 45 | // hue 0-359, sat 0-255, val 0-255 46 | //returns 24bpp color information 47 | uint32_t getPixelColorHsv(uint16_t hue, uint8_t sat, uint8_t val) 48 | { 49 | val = dim_curve[val]; 50 | sat = 255 - dim_curve[255 - sat]; 51 | 52 | int r; 53 | int g; 54 | int b; 55 | int base; 56 | 57 | if (sat == 0) 58 | { // Acromatic color (gray). Hue doesn't mind. 59 | r = val; 60 | g = val; 61 | b = val; 62 | } 63 | else 64 | { 65 | 66 | base = ((255 - sat) * val) >> 8; 67 | 68 | switch (hue / 60) 69 | { 70 | case 0: 71 | r = val; 72 | g = (((val - base) * hue) / 60) + base; 73 | b = base; 74 | break; 75 | 76 | case 1: 77 | r = (((val - base) * (60 - (hue % 60))) / 60) + base; 78 | g = val; 79 | b = base; 80 | break; 81 | 82 | case 2: 83 | r = base; 84 | g = val; 85 | b = (((val - base) * (hue % 60)) / 60) + base; 86 | break; 87 | 88 | case 3: 89 | r = base; 90 | g = (((val - base) * (60 - (hue % 60))) / 60) + base; 91 | b = val; 92 | break; 93 | 94 | case 4: 95 | r = (((val - base) * (hue % 60)) / 60) + base; 96 | g = base; 97 | b = val; 98 | break; 99 | 100 | case 5: 101 | r = val; 102 | g = base; 103 | b = (((val - base) * (60 - (hue % 60))) / 60) + base; 104 | break; 105 | } 106 | } 107 | return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; 108 | } 109 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 17 | 18 |
19 |

Smart BMS monitor


20 |

Sensor data:


21 | 0V 22 |

Vyliz mi prdel


23 | 24 |
Update firmware 25 |
26 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /mydatatypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * IncFile1.h 3 | * 4 | * Created: 6.3.2019 11:58:57 5 | * Author: z003knyu 6 | */ 7 | 8 | #ifndef mydatatypes_H_ 9 | #define mydatatypes_H_ 10 | 11 | typedef struct 12 | { 13 | byte start; 14 | byte type; 15 | byte status; 16 | byte dataLen; 17 | } bmsPacketHeaderStruct; 18 | 19 | typedef struct 20 | { 21 | uint16_t Volts; // unit 1mV 22 | int32_t Amps; // unit 1mA 23 | int32_t Watts; // unit 1W 24 | uint32_t CapacityRemainAh; 25 | uint8_t CapacityRemainPercent; //unit 1% 26 | uint32_t CapacityRemainWh; //unit Wh 27 | uint16_t Temp1; //unit 0.1C 28 | uint16_t Temp2; //unit 0.1C 29 | uint16_t BalanceCodeLow; 30 | uint16_t BalanceCodeHigh; 31 | uint8_t MosfetStatus; 32 | uint8_t Humidity; // unit percent 33 | uint16_t AlarmStatus; // (two_ints_into16(data[28], data[29])); // not used normally 34 | uint32_t FullChargeCapacity; 35 | uint32_t RemainingCapacity; 36 | uint32_t BalanceCurrent; // Resolution 1 mA 37 | 38 | } packBasicInfoStruct; 39 | 40 | typedef struct 41 | { 42 | uint8_t NumOfCells; 43 | uint16_t CellVolt[15]; //cell 1 has index 0 :-/ 44 | uint16_t CellMax; 45 | uint16_t CellMin; 46 | uint16_t CellDiff; // difference between highest and lowest 47 | uint16_t CellAvg; 48 | uint16_t CellMedian; 49 | uint32_t CellColor[15]; 50 | uint32_t CellColorDisbalance[15]; // green cell == median, red/violet cell => median + c_cellMaxDisbalance 51 | } packCellInfoStruct; 52 | 53 | struct packEepromStruct { 54 | uint16_t POVP; 55 | uint16_t PUVP; 56 | uint16_t COVP; 57 | uint16_t CUVP; 58 | uint16_t POVPRelease; 59 | uint16_t PUVPRelease; 60 | uint16_t COVPRelease; 61 | uint16_t CUVPRelease; 62 | uint16_t CHGOC; 63 | uint16_t DSGOC; 64 | }; 65 | 66 | #define STRINGBUFFERSIZE 300 67 | char stringBuffer[STRINGBUFFERSIZE]; 68 | 69 | const int32_t c_cellNominalVoltage = 3700; //mV 70 | 71 | const uint16_t c_cellAbsMin = 3000; 72 | const uint16_t c_cellAbsMax = 4200; 73 | 74 | const int32_t c_packMaxWatt = 1250; 75 | 76 | const uint16_t c_cellMaxDisbalance = 1500; //200; // cell different by this value from cell median is getting violet (worst) color 77 | 78 | #endif /* mydatatypes_H_ */ 79 | 80 | // Color definitions for 64k color mode 81 | // Bits 0..4 -> Blue 0..4 82 | // Bits 5..10 -> Green 0..5 83 | // Bits 11..15 -> Red 0..4 84 | 85 | #ifdef LCDDRIVER_TFT_eSPI 86 | #define TFT_BLACK 0x0000 /* 0, 0, 0 */ 87 | #define TFT_NAVY 0x000F /* 0, 0, 128 */ 88 | #define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */ 89 | #define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */ 90 | #define TFT_MAROON 0x7800 /* 128, 0, 0 */ 91 | #define TFT_PURPLE 0x780F /* 128, 0, 128 */ 92 | #define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */ 93 | #define TFT_LIGHTGREY 0xC618 /* 192, 192, 192 */ 94 | #define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */ 95 | #define TFT_BLUE 0x001F /* 0, 0, 255 */ 96 | #define TFT_GREEN 0x07E0 /* 0, 255, 0 */ 97 | #define TFT_CYAN 0x07FF /* 0, 255, 255 */ 98 | #define TFT_RED 0xF800 /* 255, 0, 0 */ 99 | #define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ 100 | #define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ 101 | #define TFT_WHITE 0xFFFF /* 255, 255, 255 */ 102 | #define TFT_ORANGE 0xFDA0 /* 255, 180, 0 */ 103 | #define TFT_GREENYELLOW 0xB7E0 /* 180, 255, 0 */ 104 | #define TFT_PINK 0xFC9F 105 | #endif 106 | 107 | #ifdef LCDDRIVER_Adafruit_GFX 108 | #define TFT_BLACK 0x0000 109 | #define TFT_BLUE 0x001F 110 | #define TFT_RED 0xF800 111 | #define TFT_GREEN 0x07E0 112 | #define TFT_CYAN 0x07FF 113 | #define TFT_MAGENTA 0xF81F 114 | #define TFT_YELLOW 0xFFE0 115 | #define TFT_WHITE 0xFFFF 116 | #endif 117 | -------------------------------------------------------------------------------- /network.ino: -------------------------------------------------------------------------------- 1 | 2 | // const char *ssid = "kolin2"; 3 | // const char *password = "hovnokleslo"; 4 | 5 | // //WebServer server(80); 6 | 7 | // void newtworkStartup() 8 | // { 9 | // lcdStartNetworking(); 10 | // WiFi.begin(ssid, password); 11 | // delay(3000); 12 | // if (WiFi.status() == WL_CONNECTED) 13 | // { 14 | // commSerial.println(""); 15 | // commSerial.print("Connected to "); 16 | // commSerial.println(ssid); 17 | // commSerial.print("IP address: "); 18 | // commSerial.println(WiFi.localIP()); 19 | // lcdNetworkStatus(0); 20 | // } 21 | // else 22 | // { 23 | // lcdNetworkStatus(1); 24 | // commSerial.println(""); 25 | // commSerial.print("failed to connect in time"); 26 | // } 27 | 28 | // server.on("/", HTTP_GET, handleIndexPage); 29 | // server.on("/firmware", HTTP_GET, handleFirmwarePage); 30 | // server.on("/firmwareupdate", HTTP_POST, handleFirmware1, handleFirmware2); 31 | // server.on("/getdata", handleData); //To get update of ADC Value only 32 | // server.begin(); 33 | // } 34 | 35 | // void handleData() 36 | // { 37 | // int a = random(1024); 38 | 39 | 40 | // //constructBigString(); 41 | // //commSerial.print(stringBuffer); 42 | // String webValue = String(a); 43 | // server.send(200, "text/plane", webValue); //Send ADC value only to client ajax request 44 | // } 45 | 46 | // void handleIndexPage() 47 | // { 48 | // String s = INDEX_page; 49 | // server.sendHeader("Connection", "close"); 50 | // server.send(200, "text/html", s); 51 | // } 52 | 53 | // void handleFirmwarePage() 54 | // { 55 | // String s = FIRMWARE_page; 56 | // server.sendHeader("Connection", "close"); 57 | // server.send(200, "text/html", s); 58 | // } 59 | 60 | // void handleFirmware1() 61 | // { 62 | // server.sendHeader("Connection", "close"); 63 | // server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); 64 | // ESP.restart(); 65 | // } 66 | 67 | // void handleFirmware2() 68 | // { 69 | // HTTPUpload &upload = server.upload(); 70 | // if (upload.status == UPLOAD_FILE_START) 71 | // { 72 | // commSerial.printf("Update: %s\n", upload.filename.c_str()); 73 | // if (!Update.begin(UPDATE_SIZE_UNKNOWN)) 74 | // { //start with max available size 75 | // Update.printError(commSerial); 76 | // } 77 | // } 78 | // else if (upload.status == UPLOAD_FILE_WRITE) 79 | // { 80 | // /* flashing firmware to ESP*/ 81 | // if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) 82 | // { 83 | // Update.printError(commSerial); 84 | // } 85 | // } 86 | // else if (upload.status == UPLOAD_FILE_END) 87 | // { 88 | // if (Update.end(true)) 89 | // { //true to set the size to the current progress 90 | // commSerial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); 91 | // } 92 | // else 93 | // { 94 | // Update.printError(commSerial); 95 | // } 96 | // } 97 | // } -------------------------------------------------------------------------------- /oled.ino: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | // // (/* rotation */, /* reset=*/, /* clock=*/ , /* data=*/ ); // ESP32 Thing, HW I2C with pin remapping 5 | 6 | // #define OLEDCONSTRUCTOR U8G2_SH1106_128X64_NONAME_F_HW_I2C 7 | 8 | // OLEDCONSTRUCTOR oled1(U8G2_R0, U8X8_PIN_NONE, 27, 33); // ESP32 Thing, HW I2C with pin remapping 9 | // OLEDCONSTRUCTOR oled2(U8G2_R0, U8X8_PIN_NONE, 27, 33); // ESP32 Thing, HW I2C with pin remapping 10 | 11 | // void oled_startup() 12 | // { 13 | // oled1.setI2CAddress(0x3C * 2); //<- SOLVED it works 14 | // oled2.setI2CAddress(0x3D * 2); //<- SOLVED it works 15 | 16 | // oled1.begin(); 17 | // oled2.begin(); 18 | // //oled1.setFont(u8g_font_6x10); 19 | // //oled2.setFont(u8g_font_6x10); 20 | // } 21 | 22 | // void showInfoOled() 23 | // { 24 | // char buffer[10]; 25 | // static uint8_t yy; 26 | // oled1.clearBuffer(); 27 | // yy++; 28 | // if (yy > 63) 29 | // { 30 | // yy = 0; 31 | // } 32 | 33 | // oled1.drawHLine(0, yy, 128); 34 | 35 | // //oled1.setFont(u8g_font_6x10); 36 | // oled1.setFont(u8g_font_10x20); 37 | // oled1.drawStr(0, 20, "OLED 1"); 38 | 39 | // dtostrf((float)packBasicInfo.Volts / 1000, 5, 2, buffer); 40 | 41 | // oled1.drawStr(0, 40, buffer); 42 | // oled1.drawStr(50, 40, "V"); 43 | 44 | // itoa(yy, buffer, 10); //number to string 45 | // oled1.drawStr(0, 60, buffer); 46 | // oled1.sendBuffer(); 47 | 48 | // oled2.clearBuffer(); 49 | // //oled2.setFont(u8g_font_6x10); 50 | // for (uint8_t i = 0; i < 12; i++) 51 | // { 52 | // oledBargraphVertical(i * 10, 36, 8, 28, packCellInfo.CellVolt[i], c_cellAbsMin, c_cellAbsMax, oled2); //packCellInfo.CellVolt[0] 53 | // } 54 | 55 | // //oled2.setFont(u8g_font_10x20); 56 | // //oled2.drawStr(0, 20, "OLED 2"); 57 | // //dtostrf(pokus, 5, 2, buffer); 58 | // //oled2.drawStr(0, 40, buffer); 59 | // //oled2.drawStr(50, 40, "V"); 60 | // oled2.sendBuffer(); 61 | // } 62 | 63 | // void oledBargraphVertical(uint8_t origin_x, uint8_t origin_y, uint8_t width, uint8_t height, uint16_t value, uint16_t valueMin, uint16_t valueMax, OLEDCONSTRUCTOR &refOled) 64 | // { 65 | // const uint8_t spacing = 2; 66 | // if (value < valueMin) 67 | // { 68 | // value = valueMin; 69 | // } 70 | // if (value > valueMax) 71 | // { 72 | // value = valueMax; 73 | // } 74 | 75 | // uint8_t box_origin_x = origin_x + spacing; 76 | // uint8_t box_origin_y = origin_y + spacing; 77 | // uint8_t box_width = width - spacing * 2; 78 | // uint8_t box_min_height = 1; 79 | // uint8_t box_max_height = height - spacing; 80 | 81 | // uint8_t nipple_origin_x = box_origin_x; 82 | // uint8_t nipple_origin_y = origin_y - spacing; 83 | // uint8_t nipple_width = box_width; 84 | // uint8_t nipple_height = spacing; 85 | 86 | // int16_t box_height = map(value, valueMax, valueMin, box_min_height, box_max_height); 87 | // box_origin_y = box_origin_y + box_height; 88 | // box_height = box_max_height - box_height - spacing; 89 | // if (box_height < 0) //ugly hack 90 | // { 91 | // box_height = 0; 92 | // } 93 | 94 | // refOled.drawFrame(origin_x, origin_y, width, height); //(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h) 95 | // refOled.drawBox(box_origin_x, box_origin_y, box_width, box_height); 96 | // refOled.drawBox(nipple_origin_x, nipple_origin_y, nipple_width, nipple_height); 97 | // } -------------------------------------------------------------------------------- /simulation.ino: -------------------------------------------------------------------------------- 1 | void bmsSimulate() 2 | { 3 | TRACE; 4 | unsigned long currentMillis = millis(); 5 | if ((currentMillis - previousMillis >= 125)) 6 | { 7 | previousMillis = currentMillis; 8 | showInfoLcd(); 9 | //showInfoOled(); 10 | //stripTest(); 11 | if (toggle) //alternate info3 and info4 12 | { 13 | bmsFakeInfo3(); 14 | //showBasicInfo(); 15 | newPacketReceived = false; 16 | } 17 | else 18 | { 19 | bmsFakeInfo4(); 20 | //showCellInfo(); 21 | } 22 | toggle = !toggle; 23 | } 24 | } 25 | 26 | 27 | void bmsFakeInfo3() 28 | { 29 | TRACE; 30 | if (newPacketReceived == true) 31 | { 32 | packBasicInfo.Volts = 48660; 33 | packBasicInfo.Amps = 15420; 34 | packBasicInfo.CapacityRemainAh = 15990; 35 | packBasicInfo.CapacityRemainPercent = 78; //in % 36 | packBasicInfo.Temp1 = 356; 37 | packBasicInfo.Temp2 = 452; 38 | //packBasicInfo.BalanceCodeLow=???; 39 | //packBasicInfo.BalanceCodeHigh=???; 40 | //packBasicInfo.MosfetStatus=???; 41 | lcdConnected(); //clear screen 42 | } 43 | else 44 | { 45 | packBasicInfo.Volts = packBasicInfo.Volts + 10; 46 | //commSerial.println(packBasicInfo.Volts); 47 | } 48 | } 49 | 50 | void bmsFakeInfo4() 51 | { 52 | 53 | TRACE; 54 | uint16_t _cellSum; 55 | uint16_t _cellMin = 5000; 56 | uint16_t _cellMax = 0; 57 | uint16_t _cellAvg; 58 | uint16_t _cellDiff; 59 | uint16_t randNum; 60 | packCellInfo.NumOfCells = 12; 61 | 62 | for (size_t i = 0; i < 12; i++) 63 | { 64 | 65 | randNum = random(c_cellAbsMax - c_cellAbsMin) + c_cellAbsMin; 66 | packCellInfo.CellVolt[i] = randNum; 67 | 68 | _cellSum += packCellInfo.CellVolt[i]; 69 | if (packCellInfo.CellVolt[i] > _cellMax) 70 | { 71 | _cellMax = packCellInfo.CellVolt[i]; 72 | } 73 | if (packCellInfo.CellVolt[i] < _cellMin) 74 | { 75 | _cellMin = packCellInfo.CellVolt[i]; 76 | } 77 | packCellInfo.CellColor[i] = getPixelColorHsv(mapHue(packCellInfo.CellVolt[i], c_cellAbsMin, c_cellAbsMax), 255, 255); 78 | } 79 | packCellInfo.CellMin = _cellMin; 80 | packCellInfo.CellMax = _cellMax; 81 | packCellInfo.CellDiff = _cellMax - _cellMin; // Resolution 10 mV -> convert to volts 82 | packCellInfo.CellAvg = _cellSum / packCellInfo.NumOfCells; 83 | //----cell median calculation---- 84 | uint16_t n = packCellInfo.NumOfCells; 85 | uint16_t i, j; 86 | uint16_t temp; 87 | uint16_t x[n]; 88 | 89 | for (uint8_t u = 0; u < n; u++) 90 | { 91 | x[u] = packCellInfo.CellVolt[u]; 92 | } 93 | 94 | for (i = 1; i <= n; ++i) //sort data 95 | { 96 | for (j = i + 1; j <= n; ++j) 97 | { 98 | if (x[i] > x[j]) 99 | { 100 | temp = x[i]; 101 | x[i] = x[j]; 102 | x[j] = temp; 103 | } 104 | } 105 | } 106 | 107 | if (n % 2 == 0) //compute median 108 | { 109 | packCellInfo.CellMedian = (x[n / 2] + x[n / 2 + 1]) / 2; 110 | } 111 | else 112 | { 113 | packCellInfo.CellMedian = x[n / 2 + 1]; 114 | } 115 | 116 | //-----voltage disbalance color calculation 117 | for (uint8_t q = 0; q < packCellInfo.NumOfCells; q++) 118 | { 119 | uint32_t disbal = abs(packCellInfo.CellMedian - packCellInfo.CellVolt[q]); 120 | packCellInfo.CellColorDisbalance[q] = getPixelColorHsv(mapHue(disbal, 0, c_cellMaxDisbalance), 255, 255); 121 | 122 | commSerial.print("median "); 123 | commSerial.println(packCellInfo.CellMedian); 124 | 125 | commSerial.print(q); 126 | commSerial.print(" uint32_t disbal: "); 127 | commSerial.println(disbal); 128 | 129 | commSerial.print(q); 130 | commSerial.print(" mapval "); 131 | commSerial.println(mapHue(disbal, 0, c_cellMaxDisbalance)); 132 | 133 | commSerial.println(); 134 | commSerial.println(); 135 | } 136 | } -------------------------------------------------------------------------------- /webpages.h: -------------------------------------------------------------------------------- 1 | const char FIRMWARE_page[] PROGMEM = R"=====( 2 | 3 | 4 | 5 |
6 | 7 | 8 |
9 | 10 | 11 | )====="; 12 | 13 | const char INDEX_page[] PROGMEM = R"=====( 14 | 15 | 16 | 17 | 29 | 30 | 31 | 32 |
33 |

Smart BMS monitor


34 |

Sensor data:


35 | 0V 36 |

Vyliz mi prdel


37 | 38 |
Update firmware 39 |
40 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | )====="; --------------------------------------------------------------------------------