├── .gitattributes ├── CRC8.h ├── CRC8.cpp ├── SerialConsole.h ├── Logger.h ├── BMSModule.h ├── BMSModuleManager.h ├── CONFIG.H ├── BMSUtil.h ├── SerialConsole.cpp ├── Logger.cpp ├── BMSModule.cpp ├── BMSModuleManager.cpp └── BMWPhevBMS.ino /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /CRC8.h: -------------------------------------------------------------------------------- 1 | /* CRC8 library 2 | 3 | */ 4 | #ifndef CRC8_H 5 | #define CRC8_H 6 | 7 | #if ARDUINO >= 100 8 | #include "Arduino.h" 9 | #else 10 | #include "WProgram.h" 11 | #endif 12 | 13 | typedef uint8_t crc; 14 | #define POLYNOMIAL 0x1D /* CRC8_CCITT -- this polynomial needs to match choice on javascript end */ 15 | #define WIDTH (8 * sizeof(crc)) 16 | #define TOPBIT (1 << (WIDTH - 1)) 17 | 18 | class CRC8 { 19 | public: 20 | CRC8(); 21 | void begin(); 22 | crc get_crc8(uint8_t const message[], int nBytes, uint8_t final); 23 | 24 | private: 25 | uint8_t crcTable[256]; 26 | 27 | 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /CRC8.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "CRC8.h" 4 | 5 | 6 | CRC8::CRC8(void) { 7 | 8 | 9 | } 10 | 11 | void CRC8::begin(void) { 12 | crc remainder; 13 | for (int dividend = 0; dividend < 256; ++dividend) 14 | { 15 | remainder = dividend << (WIDTH - 8); 16 | 17 | 18 | for (uint8_t bit = 8; bit > 0; --bit) 19 | { 20 | if (remainder & TOPBIT) 21 | { 22 | remainder = (remainder << 1) ^ POLYNOMIAL; 23 | } 24 | else 25 | { 26 | remainder = (remainder << 1); 27 | } 28 | } 29 | crcTable[dividend] = remainder; 30 | } 31 | } 32 | 33 | 34 | crc CRC8::get_crc8(uint8_t const message[], int nBytes, uint8_t final) { 35 | uint8_t data; 36 | crc remainder = 0xFF; 37 | 38 | for (int byte = 0; byte < nBytes; ++byte) 39 | { 40 | data = message[byte] ^ (remainder >> (WIDTH - 8)); 41 | remainder = crcTable[data] ^ (remainder << 8); 42 | } 43 | 44 | remainder = remainder^final; 45 | 46 | return (remainder); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /SerialConsole.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SerialConsole.h 3 | * 4 | Copyright (c) 2017 EVTV / Collin Kidder 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included 15 | in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef SERIALCONSOLE_H_ 28 | #define SERIALCONSOLE_H_ 29 | 30 | #include "config.h" 31 | 32 | class SerialConsole { 33 | public: 34 | SerialConsole(); 35 | void loop(); 36 | void printMenu(); 37 | 38 | protected: 39 | enum CONSOLE_STATE 40 | { 41 | STATE_ROOT_MENU 42 | }; 43 | 44 | private: 45 | char cmdBuffer[80]; 46 | int ptrBuffer; 47 | int state; 48 | int loopcount; 49 | bool cancel; 50 | 51 | 52 | void init(); 53 | void serialEvent(); 54 | void handleConsoleCmd(); 55 | void handleShortCmd(); 56 | }; 57 | 58 | #endif /* SERIALCONSOLE_H_ */ 59 | -------------------------------------------------------------------------------- /Logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Logger.h 3 | * 4 | Copyright (c) 2013 Collin Kidder, Michael Neuweiler, Charles Galpin 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included 15 | in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef LOGGER_H_ 28 | #define LOGGER_H_ 29 | 30 | #include 31 | #include "config.h" 32 | 33 | class Logger { 34 | public: 35 | enum LogLevel { 36 | Debug = 0, Info = 1, Warn = 2, Error = 3, Off = 4 37 | }; 38 | static void debug(char *, ...); 39 | static void info(char *, ...); 40 | static void warn(char *, ...); 41 | static void error(char *, ...); 42 | static void console(char *, ...); 43 | static void setLoglevel(LogLevel); 44 | static LogLevel getLogLevel(); 45 | static uint32_t getLastLogTime(); 46 | static boolean isDebug(); 47 | private: 48 | static LogLevel logLevel; 49 | static uint32_t lastLogTime; 50 | 51 | static void log(LogLevel, char *format, va_list); 52 | static void logMessage(char *format, va_list args); 53 | }; 54 | 55 | #endif /* LOGGER_H_ */ 56 | -------------------------------------------------------------------------------- /BMSModule.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class BMSModule 5 | { 6 | public: 7 | BMSModule(); 8 | void decodecan(int Id, CAN_message_t &msg, bool Ign); 9 | void decodetemp(CAN_message_t &msg, int CSC); 10 | void clearmodule(); 11 | void readStatus(); 12 | int getscells(); 13 | int getbalstat(); 14 | bool readModuleValues(); 15 | float getCellVoltage(int cell); 16 | float getLowCellV(); 17 | float getHighCellV(); 18 | float getAverageV(); 19 | float getLowTemp(); 20 | float getHighTemp(); 21 | float getHighestModuleVolt(); 22 | float getLowestModuleVolt(); 23 | float getHighestCellVolt(int cell); 24 | float getLowestCellVolt(int cell); 25 | float getHighestTemp(); 26 | float getLowestTemp(); 27 | float getAvgTemp(); 28 | float getModuleVoltage(); 29 | float getTemperature(int temp); 30 | uint8_t getFaults(); 31 | uint8_t getAlerts(); 32 | uint8_t getCOVCells(); 33 | uint8_t getCUVCells(); 34 | uint32_t getError(); 35 | void setAddress(int newAddr); 36 | int getAddress(); 37 | bool isExisting(); 38 | void setExists(bool ex); 39 | bool isReset(); 40 | void setReset(bool ex); 41 | void settempsensor(int tempsensor); 42 | void setIgnoreCell(float Ignore); 43 | void setTempOff( int16_t tempoff); 44 | 45 | 46 | private: 47 | float cellVolt[16]; // calculated as 16 bit value * 6.250 / 16383 = volts 48 | float lowestCellVolt[16]; 49 | float highestCellVolt[16]; 50 | float moduleVolt; // calculated as 16 bit value * 33.333 / 16383 = volts 51 | float temperatures[4]; // Don't know the proper scaling at this point 52 | float lowestTemperature; 53 | float highestTemperature; 54 | float lowestModuleVolt; 55 | float highestModuleVolt; 56 | float IgnoreCell; 57 | bool exists; 58 | bool reset; 59 | int alerts; 60 | int faults; 61 | int COVFaults; 62 | int CUVFaults; 63 | int sensor; 64 | uint8_t moduleAddress; //1 to 0x3E 65 | int scells; 66 | int balstat; 67 | uint32_t error; 68 | int variant; 69 | int16_t TempOff; 70 | }; 71 | -------------------------------------------------------------------------------- /BMSModuleManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "config.h" 3 | #include "BMSModule.h" 4 | #include 5 | 6 | class BMSModuleManager 7 | { 8 | public: 9 | BMSModuleManager(); 10 | int seriescells(); 11 | void clearmodules(); 12 | void decodecan(CAN_message_t &msg,int debug); 13 | void decodetemp(CAN_message_t &msg, int debug, int CSC); 14 | void balanceCells(); 15 | void setupBoards(); 16 | bool checkcomms(); 17 | bool checkstatus(); 18 | void findBoards(); 19 | void renumberBoardIDs(); 20 | void getAllVoltTemp(); 21 | void readSetpoints(); 22 | void setBatteryID(int id); 23 | void setBalIgnore(bool BalIgn); 24 | void setPstrings(int Pstrings); 25 | void setUnderVolt(float newVal); 26 | void setOverVolt(float newVal); 27 | void setOverTemp(float newVal); 28 | void setBalanceV(float newVal); 29 | void setBalanceHyst(float newVal); 30 | void setSensors(int sensor,float Ignore, int tempoff); 31 | float getPackVoltage(); 32 | float getAvgTemperature(); 33 | float getHighTemperature(); 34 | float getLowTemperature(); 35 | float getAvgCellVolt(); 36 | float getLowCellVolt(); 37 | float getHighCellVolt(); 38 | float getHighVoltage(); 39 | float getLowVoltage(); 40 | /* 41 | void processCANMsg(CAN_FRAME &frame); 42 | */ 43 | void printAllCSV(unsigned long timestamp,float current, int SOC); 44 | void printPackSummary(); 45 | void printPackDetails(int digits,int CSCvariant); 46 | int getNumModules(); 47 | 48 | 49 | 50 | private: 51 | bool BalIgnore; 52 | float packVolt; // All modules added together 53 | int Pstring; 54 | float LowCellVolt; 55 | float HighCellVolt; 56 | float lowestPackVolt; 57 | float highestPackVolt; 58 | float lowestPackTemp; 59 | float highestPackTemp; 60 | float highTemp; 61 | float lowTemp; 62 | BMSModule modules[MAX_MODULE_ADDR + 1]; // store data for as many modules as we've configured for. 63 | int batteryID; 64 | int numFoundModules; // The number of modules that seem to exist 65 | bool isFaulted; 66 | int spack; 67 | /* 68 | void sendBatterySummary(); 69 | void sendModuleSummary(int module); 70 | void sendCellDetails(int module, int cell); 71 | */ 72 | 73 | }; 74 | -------------------------------------------------------------------------------- /CONFIG.H: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | //Set to the proper port for your USB connection - SerialUSB on Due (Native) or Serial for Due (Programming) or Teensy 6 | #define SERIALCONSOLE Serial 7 | 8 | //Define this to be the serial port the Tesla BMS modules are connected to. 9 | //On the Due you need to use a USART port (Serial1, Serial2, Serial3) and update the call to serialSpecialInit if not Serial1 10 | //Serial3 for teensy 11 | #define SERIALBMS Serial3 12 | 13 | // victron serial VE direct bus config 14 | #define VE Serial2 15 | 16 | #define REG_DEV_STATUS 1 17 | #define REG_GPAI 1 18 | #define REG_VCELL1 3 19 | #define REG_VCELL2 5 20 | #define REG_VCELL3 7 21 | #define REG_VCELL4 9 22 | #define REG_VCELL5 0xB 23 | #define REG_VCELL6 0xD 24 | #define REG_TEMPERATURE1 0xF 25 | #define REG_TEMPERATURE2 0x11 26 | #define REG_ALERT_STATUS 0x20 27 | #define REG_FAULT_STATUS 0x21 28 | #define REG_COV_FAULT 0x22 29 | #define REG_CUV_FAULT 0x23 30 | #define REG_ADC_CTRL 0x30 31 | #define REG_IO_CTRL 0x31 32 | #define REG_BAL_CTRL 0x32 33 | #define REG_BAL_TIME 0x33 34 | #define REG_ADC_CONV 0x34 35 | #define REG_ADDR_CTRL 0x3B 36 | 37 | #define MAX_MODULE_ADDR 0x3E 38 | 39 | #define EEPROM_VERSION 0x11 //update any time EEPROM struct below is changed. 40 | #define EEPROM_PAGE 0 41 | 42 | typedef struct { 43 | uint8_t version; 44 | uint8_t checksum; 45 | uint32_t canSpeed; 46 | uint8_t batteryID; //which battery ID should this board associate as on the CAN bus 47 | uint8_t logLevel; 48 | float OverVSetpoint; 49 | float UnderVSetpoint; 50 | float DischHys; 51 | float ChargeVsetpoint; 52 | float DischVsetpoint; 53 | float ChargeHys; 54 | float StoreVsetpoint; 55 | float WarnOff; 56 | float OverTSetpoint; 57 | float UnderTSetpoint; 58 | uint16_t triptime; 59 | float ChargeTSetpoint; 60 | float DisTSetpoint; 61 | float WarnToff; 62 | float CellGap; 63 | uint8_t IgnoreTemp; 64 | float IgnoreVolt; 65 | float balanceVoltage; 66 | float balanceHyst; 67 | int Scells; 68 | int Pstrings; 69 | int CAP; 70 | int chargecurrentmax; 71 | uint16_t chargecurrent2max; 72 | int chargecurrentend; 73 | int discurrentmax ; 74 | int socvolt[4]; 75 | int invertcur; 76 | int cursens; 77 | int curcan; 78 | int voltsoc; 79 | int Pretime; 80 | int conthold; 81 | int Precurrent; 82 | float convhigh; 83 | float convlow; 84 | int32_t changecur; 85 | uint16_t offset1; 86 | uint16_t offset2; 87 | uint16_t balanceDuty; 88 | int ESSmode; 89 | int gaugelow; 90 | int gaugehigh; 91 | int ncur; 92 | int chargertype; 93 | int chargerspd; 94 | uint16_t UnderDur; 95 | uint16_t CurDead; 96 | float DisTaper; 97 | bool ChargerDirect; 98 | uint8_t tripcont; 99 | uint8_t CSCvariant; 100 | int16_t TempOff; 101 | int chargereff; 102 | int chargerACv; 103 | } EEPROMSettings; 104 | -------------------------------------------------------------------------------- /BMSUtil.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Logger.h" 3 | 4 | class BMSUtil { 5 | public: 6 | 7 | static uint8_t genCRC(uint8_t *input, int lenInput) 8 | { 9 | uint8_t generator = 0x07; 10 | uint8_t crc = 0; 11 | 12 | for (int x = 0; x < lenInput; x++) 13 | { 14 | crc ^= input[x]; /* XOR-in the next input byte */ 15 | 16 | for (int i = 0; i < 8; i++) 17 | { 18 | if ((crc & 0x80) != 0) 19 | { 20 | crc = (uint8_t)((crc << 1) ^ generator); 21 | } 22 | else 23 | { 24 | crc <<= 1; 25 | } 26 | } 27 | } 28 | 29 | return crc; 30 | } 31 | 32 | static void sendData(uint8_t *data, uint8_t dataLen, bool isWrite) 33 | { 34 | uint8_t orig = data[0]; 35 | uint8_t addrByte = data[0]; 36 | if (isWrite) addrByte |= 1; 37 | SERIALBMS.write(addrByte); 38 | SERIALBMS.write(&data[1], dataLen - 1); //assumes that there are at least 2 bytes sent every time. There should be, addr and cmd at the least. 39 | data[0] = addrByte; 40 | if (isWrite) SERIALBMS.write(genCRC(data, dataLen)); 41 | 42 | if (Logger::isDebug()) 43 | { 44 | SERIALCONSOLE.print("Sending: "); 45 | SERIALCONSOLE.print(addrByte, HEX); 46 | SERIALCONSOLE.print(" "); 47 | for (int x = 1; x < dataLen; x++) { 48 | SERIALCONSOLE.print(data[x], HEX); 49 | SERIALCONSOLE.print(" "); 50 | } 51 | if (isWrite) SERIALCONSOLE.print(genCRC(data, dataLen), HEX); 52 | SERIALCONSOLE.println(); 53 | } 54 | 55 | data[0] = orig; 56 | } 57 | 58 | static int getReply(uint8_t *data, int maxLen) 59 | { 60 | int numBytes = 0; 61 | if (Logger::isDebug()) SERIALCONSOLE.print("Reply: "); 62 | while (SERIALBMS.available() && numBytes < maxLen) 63 | { 64 | data[numBytes] = SERIALBMS.read(); 65 | if (Logger::isDebug()) { 66 | SERIALCONSOLE.print(data[numBytes], HEX); 67 | SERIALCONSOLE.print(" "); 68 | } 69 | numBytes++; 70 | } 71 | if (maxLen == numBytes) 72 | { 73 | while (SERIALBMS.available()) SERIALBMS.read(); 74 | } 75 | if (Logger::isDebug()) SERIALCONSOLE.println(); 76 | return numBytes; 77 | } 78 | 79 | //Uses above functions to send data then get the response. Will auto retry if response not 80 | //the expected return length. This helps to alleviate any comm issues. The Due cannot exactly 81 | //match the correct comm speed so sometimes there are data glitches. 82 | static int sendDataWithReply(uint8_t *data, uint8_t dataLen, bool isWrite, uint8_t *retData, int retLen) 83 | { 84 | int attempts = 1; 85 | int returnedLength; 86 | while (attempts < 4) 87 | { 88 | sendData(data, dataLen, isWrite); 89 | delay(2 * ((retLen / 8) + 1)); 90 | returnedLength = getReply(retData, retLen); 91 | if (returnedLength == retLen) return returnedLength; 92 | attempts++; 93 | } 94 | return returnedLength; //failed to get a proper response. 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /SerialConsole.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SerialConsole.cpp 3 | * 4 | Copyright (c) 2017 EVTV / Collin Kidder 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included 15 | in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | #include "SerialConsole.h" 27 | #include "Logger.h" 28 | #include "BMSModuleManager.h" 29 | 30 | template inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; } //Lets us stream SerialUSB 31 | 32 | extern BMSModuleManager bms; 33 | 34 | bool printPrettyDisplay; 35 | uint32_t prettyCounter; 36 | int whichDisplay; 37 | 38 | SerialConsole::SerialConsole() { 39 | init(); 40 | } 41 | 42 | void SerialConsole::init() { 43 | //State variables for serial console 44 | ptrBuffer = 0; 45 | state = STATE_ROOT_MENU; 46 | loopcount=0; 47 | cancel=false; 48 | printPrettyDisplay = false; 49 | prettyCounter = 0; 50 | whichDisplay = 0; 51 | } 52 | 53 | void SerialConsole::loop() { 54 | 55 | } 56 | 57 | void SerialConsole::printMenu() { 58 | Logger::console("\n*************SYSTEM MENU *****************"); 59 | Logger::console("Enable line endings of some sort (LF, CR, CRLF)"); 60 | Logger::console("Most commands case sensitive\n"); 61 | Logger::console("GENERAL SYSTEM CONFIGURATION\n"); 62 | Logger::console(" h = help (displays this message)"); 63 | Logger::console(" S = Sleep all boards"); 64 | Logger::console(" W = Wake up all boards"); 65 | Logger::console(" C = Clear all board faults"); 66 | Logger::console(" F = Find all connected boards"); 67 | Logger::console(" R = Renumber connected boards in sequence"); 68 | Logger::console(" B = Attempt balancing for 5 seconds"); 69 | Logger::console(" p = Toggle output of pack summary every 3 seconds"); 70 | Logger::console(" d = Toggle output of pack details every 3 seconds"); 71 | 72 | Logger::console(" LOGLEVEL=%i - set log level (0=debug, 1=info, 2=warn, 3=error, 4=off)", Logger::getLogLevel()); 73 | 74 | float OverVSetpoint; 75 | float UnderVSetpoint; 76 | float OverTSetpoint; 77 | float UnderTSetpoint; 78 | float balanceVoltage; 79 | float balanceHyst; 80 | } 81 | 82 | /* There is a help menu (press H or h or ?) 83 | 84 | Commands are submitted by sending line ending (LF, CR, or both) 85 | */ 86 | void SerialConsole::serialEvent() { 87 | int incoming; 88 | incoming = SERIALCONSOLE.read(); 89 | if (incoming == -1) { //false alarm.... 90 | return; 91 | } 92 | 93 | if (incoming == 10 || incoming == 13) { //command done. Parse it. 94 | handleConsoleCmd(); 95 | ptrBuffer = 0; //reset line counter once the line has been processed 96 | } else { 97 | cmdBuffer[ptrBuffer++] = (unsigned char) incoming; 98 | if (ptrBuffer > 79) 99 | ptrBuffer = 79; 100 | } 101 | } 102 | 103 | void SerialConsole::handleConsoleCmd() { 104 | 105 | if (state == STATE_ROOT_MENU) { 106 | if (ptrBuffer == 1) { //command is a single ascii character 107 | handleShortCmd(); 108 | } else { //if cmd over 1 char then assume (for now) that it is a config line 109 | //handleConfigCmd(); 110 | } 111 | } 112 | } 113 | 114 | void SerialConsole::handleShortCmd() 115 | { 116 | uint8_t val; 117 | 118 | switch (cmdBuffer[0]) 119 | { 120 | case 'h': 121 | case '?': 122 | case 'H': 123 | printMenu(); 124 | break; 125 | case 'R': 126 | Logger::console("Renumbering all boards."); 127 | bms.renumberBoardIDs(); 128 | break; 129 | case 'p': 130 | if (whichDisplay == 1 && printPrettyDisplay) whichDisplay = 0; 131 | else 132 | { 133 | printPrettyDisplay = !printPrettyDisplay; 134 | if (printPrettyDisplay) 135 | { 136 | Logger::console("Enabling pack summary display, 5 second interval"); 137 | } 138 | else 139 | { 140 | Logger::console("No longer displaying pack summary."); 141 | } 142 | } 143 | break; 144 | case 'd': 145 | if (whichDisplay == 0 && printPrettyDisplay) whichDisplay = 1; 146 | else 147 | { 148 | printPrettyDisplay = !printPrettyDisplay; 149 | whichDisplay = 1; 150 | if (printPrettyDisplay) 151 | { 152 | Logger::console("Enabling pack details display, 5 second interval"); 153 | } 154 | else 155 | { 156 | Logger::console("No longer displaying pack details."); 157 | } 158 | } 159 | break; 160 | } 161 | } 162 | 163 | /* 164 | if (SERIALCONSOLE.available()) 165 | { 166 | char y = SERIALCONSOLE.read(); 167 | switch (y) 168 | { 169 | case '1': //ascii 1 170 | renumberBoardIDs(); // force renumber and read out 171 | break; 172 | case '2': //ascii 2 173 | SERIALCONSOLE.println(); 174 | findBoards(); 175 | break; 176 | case '3': //activate cell balance for 5 seconds 177 | SERIALCONSOLE.println(); 178 | SERIALCONSOLE.println("Balancing"); 179 | cellBalance(); 180 | break; 181 | case '4': //clear all faults on all boards, required after Reset or FPO (first power on) 182 | SERIALCONSOLE.println(); 183 | SERIALCONSOLE.println("Clearing Faults"); 184 | clearFaults(); 185 | break; 186 | 187 | case '5': //read out the status of first board 188 | SERIALCONSOLE.println(); 189 | SERIALCONSOLE.println("Reading status"); 190 | readStatus(1); 191 | break; 192 | 193 | case '6': //Read out the limit setpoints of first board 194 | SERIALCONSOLE.println(); 195 | SERIALCONSOLE.println("Reading Setpoints"); 196 | readSetpoint(1); 197 | SERIALCONSOLE.println(OVolt); 198 | SERIALCONSOLE.println(UVolt); 199 | SERIALCONSOLE.println(Tset); 200 | break; 201 | 202 | case '0': //Send all boards into Sleep state 203 | Serial.println(); 204 | Serial.println("Sleep Mode"); 205 | sleepBoards(); 206 | break; 207 | 208 | case '9'://Pull all boards out of Sleep state 209 | Serial.println(); 210 | Serial.println("Wake Boards"); 211 | wakeBoards(); 212 | break; 213 | 214 | } 215 | } 216 | */ 217 | -------------------------------------------------------------------------------- /Logger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Logger.cpp 3 | 4 | Copyright (c) 2013 Collin Kidder, Michael Neuweiler, Charles Galpin 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included 15 | in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | #include "Logger.h" 28 | 29 | Logger::LogLevel Logger::logLevel = Logger::Info; 30 | uint32_t Logger::lastLogTime = 0; 31 | 32 | /* 33 | Output a debug message with a variable amount of parameters. 34 | printf() style, see Logger::log() 35 | 36 | */ 37 | void Logger::debug(char *message, ...) { 38 | if (logLevel > Debug) 39 | return; 40 | va_list args; 41 | va_start(args, message); 42 | Logger::log(Debug, message, args); 43 | va_end(args); 44 | } 45 | 46 | /* 47 | Output a info message with a variable amount of parameters 48 | printf() style, see Logger::log() 49 | */ 50 | void Logger::info(char *message, ...) { 51 | if (logLevel > Info) 52 | return; 53 | va_list args; 54 | va_start(args, message); 55 | Logger::log(Info, message, args); 56 | va_end(args); 57 | } 58 | 59 | /* 60 | Output a warning message with a variable amount of parameters 61 | printf() style, see Logger::log() 62 | */ 63 | void Logger::warn(char *message, ...) { 64 | if (logLevel > Warn) 65 | return; 66 | va_list args; 67 | va_start(args, message); 68 | Logger::log(Warn, message, args); 69 | va_end(args); 70 | } 71 | 72 | /* 73 | Output a error message with a variable amount of parameters 74 | printf() style, see Logger::log() 75 | */ 76 | void Logger::error(char *message, ...) { 77 | if (logLevel > Error) 78 | return; 79 | va_list args; 80 | va_start(args, message); 81 | Logger::log(Error, message, args); 82 | va_end(args); 83 | } 84 | 85 | /* 86 | Output a comnsole message with a variable amount of parameters 87 | printf() style, see Logger::logMessage() 88 | */ 89 | void Logger::console(char *message, ...) { 90 | va_list args; 91 | va_start(args, message); 92 | Logger::logMessage(message, args); 93 | va_end(args); 94 | } 95 | 96 | /* 97 | Set the log level. Any output below the specified log level will be omitted. 98 | */ 99 | void Logger::setLoglevel(LogLevel level) { 100 | logLevel = level; 101 | } 102 | 103 | /* 104 | Retrieve the current log level. 105 | */ 106 | Logger::LogLevel Logger::getLogLevel() { 107 | return logLevel; 108 | } 109 | 110 | /* 111 | Return a timestamp when the last log entry was made. 112 | */ 113 | uint32_t Logger::getLastLogTime() { 114 | return lastLogTime; 115 | } 116 | 117 | /* 118 | Returns if debug log level is enabled. This can be used in time critical 119 | situations to prevent unnecessary string concatenation (if the message won't 120 | be logged in the end). 121 | 122 | Example: 123 | if (Logger::isDebug()) { 124 | Logger::debug("current time: %d", millis()); 125 | } 126 | */ 127 | boolean Logger::isDebug() { 128 | return logLevel == Debug; 129 | } 130 | 131 | /* 132 | Output a log message (called by debug(), info(), warn(), error(), console()) 133 | 134 | Supports printf() like syntax: 135 | 136 | %% - outputs a '%' character 137 | %s - prints the next parameter as string 138 | %d - prints the next parameter as decimal 139 | %f - prints the next parameter as double float 140 | %x - prints the next parameter as hex value 141 | %X - prints the next parameter as hex value with '0x' added before 142 | %b - prints the next parameter as binary value 143 | %B - prints the next parameter as binary value with '0b' added before 144 | %l - prints the next parameter as long 145 | %c - prints the next parameter as a character 146 | %t - prints the next parameter as boolean ('T' or 'F') 147 | %T - prints the next parameter as boolean ('true' or 'false') 148 | */ 149 | void Logger::log(LogLevel level, char *format, va_list args) { 150 | lastLogTime = millis(); 151 | SERIALCONSOLE.print(lastLogTime); 152 | SERIALCONSOLE.print(" - "); 153 | 154 | switch (level) { 155 | case Debug: 156 | SERIALCONSOLE.print("DEBUG"); 157 | break; 158 | case Info: 159 | SERIALCONSOLE.print("INFO"); 160 | break; 161 | case Warn: 162 | SERIALCONSOLE.print("WARNING"); 163 | break; 164 | case Error: 165 | SERIALCONSOLE.print("ERROR"); 166 | break; 167 | } 168 | SERIALCONSOLE.print(": "); 169 | 170 | logMessage(format, args); 171 | } 172 | 173 | /* 174 | Output a log message (called by log(), console()) 175 | 176 | Supports printf() like syntax: 177 | 178 | %% - outputs a '%' character 179 | %s - prints the next parameter as string 180 | %d - prints the next parameter as decimal 181 | %f - prints the next parameter as double float 182 | %x - prints the next parameter as hex value 183 | %X - prints the next parameter as hex value with '0x' added before 184 | %b - prints the next parameter as binary value 185 | %B - prints the next parameter as binary value with '0b' added before 186 | %l - prints the next parameter as long 187 | %c - prints the next parameter as a character 188 | %t - prints the next parameter as boolean ('T' or 'F') 189 | %T - prints the next parameter as boolean ('true' or 'false') 190 | */ 191 | void Logger::logMessage(char *format, va_list args) { 192 | for (; *format != 0; ++format) { 193 | if (*format == '%') { 194 | ++format; 195 | if (*format == '\0') 196 | break; 197 | if (*format == '%') { 198 | SERIALCONSOLE.print(*format); 199 | continue; 200 | } 201 | if (*format == 's') { 202 | register char *s = (char *) va_arg( args, int ); 203 | SERIALCONSOLE.print(s); 204 | continue; 205 | } 206 | if (*format == 'd' || *format == 'i') { 207 | SERIALCONSOLE.print(va_arg( args, int ), DEC); 208 | continue; 209 | } 210 | if (*format == 'f') { 211 | SERIALCONSOLE.print(va_arg( args, double ), 3); 212 | continue; 213 | } 214 | if (*format == 'z') { 215 | SERIALCONSOLE.print(va_arg( args, double ), 0); 216 | continue; 217 | } 218 | if (*format == 'x') { 219 | SERIALCONSOLE.print(va_arg( args, int ), HEX); 220 | continue; 221 | } 222 | if (*format == 'X') { 223 | SERIALCONSOLE.print("0x"); 224 | SERIALCONSOLE.print(va_arg( args, int ), HEX); 225 | continue; 226 | } 227 | if (*format == 'b') { 228 | SERIALCONSOLE.print(va_arg( args, int ), BIN); 229 | continue; 230 | } 231 | if (*format == 'B') { 232 | SERIALCONSOLE.print("0b"); 233 | SERIALCONSOLE.print(va_arg( args, int ), BIN); 234 | continue; 235 | } 236 | if (*format == 'l') { 237 | SERIALCONSOLE.print(va_arg( args, long ), DEC); 238 | continue; 239 | } 240 | 241 | if (*format == 'c') { 242 | SERIALCONSOLE.print(va_arg( args, int )); 243 | continue; 244 | } 245 | if (*format == 't') { 246 | if (va_arg( args, int ) == 1) { 247 | SERIALCONSOLE.print("T"); 248 | } else { 249 | SERIALCONSOLE.print("F"); 250 | } 251 | continue; 252 | } 253 | if (*format == 'T') { 254 | if (va_arg( args, int ) == 1) { 255 | SERIALCONSOLE.print("TRUE"); 256 | } else { 257 | SERIALCONSOLE.print("FALSE"); 258 | } 259 | continue; 260 | } 261 | 262 | } 263 | SERIALCONSOLE.print(*format); 264 | } 265 | SERIALCONSOLE.println(); 266 | } 267 | -------------------------------------------------------------------------------- /BMSModule.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "BMSModule.h" 3 | #include "BMSUtil.h" 4 | #include "Logger.h" 5 | 6 | 7 | BMSModule::BMSModule() 8 | { 9 | for (int i = 0; i < 16; i++) 10 | { 11 | cellVolt[i] = 0.0f; 12 | lowestCellVolt[i] = 5.0f; 13 | highestCellVolt[i] = 0.0f; 14 | } 15 | moduleVolt = 0.0f; 16 | temperatures[0] = 0.0f; 17 | temperatures[1] = 0.0f; 18 | temperatures[2] = 0.0f; 19 | temperatures[3] = 0.0f; 20 | lowestTemperature = 200.0f; 21 | highestTemperature = -100.0f; 22 | lowestModuleVolt = 200.0f; 23 | highestModuleVolt = 0.0f; 24 | exists = false; 25 | reset = false; 26 | moduleAddress = 0; 27 | error = 0; 28 | } 29 | 30 | void BMSModule::clearmodule() 31 | { 32 | for (int i = 0; i < 16; i++) 33 | { 34 | cellVolt[i] = 0.0f; 35 | } 36 | moduleVolt = 0.0f; 37 | temperatures[0] = 0.0f; 38 | temperatures[1] = 0.0f; 39 | temperatures[2] = 0.0f; 40 | temperatures[3] = 0.0f; 41 | variant = 0; 42 | exists = false; 43 | reset = false; 44 | moduleAddress = 0; 45 | } 46 | 47 | void BMSModule::decodetemp(CAN_message_t &msg, int CSC) 48 | { 49 | for (int g = 0; g < 4; g++) 50 | { 51 | temperatures[g] = msg.buf[g] - 40; 52 | if (temperatures[g] > -40) 53 | { 54 | temperatures[g] = temperatures[g] + TempOff; 55 | } 56 | } 57 | } 58 | 59 | void BMSModule::decodecan(int Id, CAN_message_t &msg, bool Ign) 60 | { 61 | switch (Id) 62 | { 63 | case 0: 64 | error = msg.buf[0] + (msg.buf[1] << 8) + (msg.buf[2] << 16) + (msg.buf[3] << 24); 65 | balstat = (msg.buf[5]<< 8) + msg.buf[4]; 66 | break; 67 | 68 | case 1: 69 | if (balstat == 0 && Ign == 0) 70 | { 71 | cellVolt[0] = float(msg.buf[0] + (msg.buf[1] & 0x3F) * 256) / 1000; 72 | cellVolt[1] = float(msg.buf[2] + (msg.buf[3] & 0x3F) * 256) / 1000; 73 | cellVolt[2] = float(msg.buf[4] + (msg.buf[5] & 0x3F) * 256) / 1000; 74 | } 75 | break; 76 | 77 | case 2: 78 | if (balstat == 0 && Ign == 0) 79 | { 80 | cellVolt[3] = float(msg.buf[0] + (msg.buf[1] & 0x3F) * 256) / 1000; 81 | cellVolt[4] = float(msg.buf[2] + (msg.buf[3] & 0x3F) * 256) / 1000; 82 | cellVolt[5] = float(msg.buf[4] + (msg.buf[5] & 0x3F) * 256) / 1000; 83 | } 84 | break; 85 | 86 | case 3: 87 | if (balstat == 0 && Ign == 0) 88 | { 89 | cellVolt[6] = float(msg.buf[0] + (msg.buf[1] & 0x3F) * 256) / 1000; 90 | cellVolt[7] = float(msg.buf[2] + (msg.buf[3] & 0x3F) * 256) / 1000; 91 | cellVolt[8] = float(msg.buf[4] + (msg.buf[5] & 0x3F) * 256) / 1000; 92 | } 93 | break; 94 | 95 | case 4: 96 | if (balstat == 0 && Ign == 0) 97 | { 98 | cellVolt[9] = float(msg.buf[0] + (msg.buf[1] & 0x3F) * 256) / 1000; 99 | cellVolt[10] = float(msg.buf[2] + (msg.buf[3] & 0x3F) * 256) / 1000; 100 | cellVolt[11] = float(msg.buf[4] + (msg.buf[5] & 0x3F) * 256) / 1000; 101 | } 102 | break; 103 | 104 | case 5: 105 | if (balstat == 0 && Ign == 0) 106 | { 107 | cellVolt[12] = float(msg.buf[0] + (msg.buf[1] & 0x3F) * 256) / 1000; 108 | cellVolt[13] = float(msg.buf[2] + (msg.buf[3] & 0x3F) * 256) / 1000; 109 | cellVolt[14] = float(msg.buf[4] + (msg.buf[5] & 0x3F) * 256) / 1000; 110 | } 111 | break; 112 | 113 | 114 | case 6: 115 | if (balstat == 0 && Ign == 0) 116 | { 117 | cellVolt[15] = float(msg.buf[0] + (msg.buf[1] & 0x3F) * 256) / 1000; 118 | } 119 | break; 120 | 121 | default: 122 | 123 | break; 124 | } 125 | for (int i = 0; i < 16; i++) 126 | { 127 | if (lowestCellVolt[i] > cellVolt[i] && cellVolt[i] >= IgnoreCell) lowestCellVolt[i] = cellVolt[i]; 128 | if (highestCellVolt[i] < cellVolt[i] && cellVolt[i] > 5.0) highestCellVolt[i] = cellVolt[i]; 129 | } 130 | } 131 | 132 | 133 | /* 134 | Reading the status of the board to identify any flags, will be more useful when implementing a sleep cycle 135 | */ 136 | 137 | uint8_t BMSModule::getFaults() 138 | { 139 | return faults; 140 | } 141 | 142 | uint8_t BMSModule::getAlerts() 143 | { 144 | return alerts; 145 | } 146 | 147 | uint8_t BMSModule::getCOVCells() 148 | { 149 | return COVFaults; 150 | } 151 | 152 | uint8_t BMSModule::getCUVCells() 153 | { 154 | return CUVFaults; 155 | } 156 | 157 | /* 158 | Reading the setpoints, after a reset the default tesla setpoints are loaded 159 | Default response : 0x10, 0x80, 0x31, 0x81, 0x08, 0x81, 0x66, 0xff 160 | */ 161 | /* 162 | void BMSModule::readSetpoint() 163 | { 164 | uint8_t payload[3]; 165 | uint8_t buff[12]; 166 | payload[0] = moduleAddress << 1; //adresss 167 | payload[1] = 0x40;//Alert Status start 168 | payload[2] = 0x08;//two registers 169 | sendData(payload, 3, false); 170 | delay(2); 171 | getReply(buff); 172 | 173 | OVolt = 2.0+ (0.05* buff[5]); 174 | UVolt = 0.7 + (0.1* buff[7]); 175 | Tset = 35 + (5 * (buff[9] >> 4)); 176 | } */ 177 | 178 | float BMSModule::getCellVoltage(int cell) 179 | { 180 | if (cell < 0 || cell > 16) return 0.0f; 181 | return cellVolt[cell]; 182 | } 183 | 184 | float BMSModule::getLowCellV() 185 | { 186 | float lowVal = 10.0f; 187 | for (int i = 0; i < 16; i++) if (cellVolt[i] < lowVal && cellVolt[i] > IgnoreCell) lowVal = cellVolt[i]; 188 | return lowVal; 189 | } 190 | 191 | float BMSModule::getHighCellV() 192 | { 193 | float hiVal = 0.0f; 194 | for (int i = 0; i < 16; i++) 195 | if (cellVolt[i] > IgnoreCell && cellVolt[i] < 5.0) 196 | { 197 | if (cellVolt[i] > hiVal) hiVal = cellVolt[i]; 198 | } 199 | return hiVal; 200 | } 201 | 202 | float BMSModule::getAverageV() 203 | { 204 | int x = 0; 205 | float avgVal = 0.0f; 206 | for (int i = 0; i < 16; i++) 207 | { 208 | if (cellVolt[i] > IgnoreCell && cellVolt[i] < 5.0) 209 | { 210 | x++; 211 | avgVal += cellVolt[i]; 212 | } 213 | } 214 | 215 | scells = x; 216 | avgVal /= x; 217 | return avgVal; 218 | } 219 | 220 | int BMSModule::getscells() 221 | { 222 | return scells; 223 | } 224 | 225 | int BMSModule::getbalstat() 226 | { 227 | return balstat; 228 | } 229 | 230 | float BMSModule::getHighestModuleVolt() 231 | { 232 | return highestModuleVolt; 233 | } 234 | 235 | float BMSModule::getLowestModuleVolt() 236 | { 237 | return lowestModuleVolt; 238 | } 239 | 240 | float BMSModule::getHighestCellVolt(int cell) 241 | { 242 | if (cell < 0 || cell > 16) return 0.0f; 243 | return highestCellVolt[cell]; 244 | } 245 | 246 | float BMSModule::getLowestCellVolt(int cell) 247 | { 248 | if (cell < 0 || cell > 16) return 0.0f; 249 | return lowestCellVolt[cell]; 250 | } 251 | 252 | float BMSModule::getHighestTemp() 253 | { 254 | return highestTemperature; 255 | } 256 | 257 | float BMSModule::getLowestTemp() 258 | { 259 | return lowestTemperature; 260 | } 261 | 262 | float BMSModule::getLowTemp() 263 | { 264 | /* 265 | float templow = 9999; 266 | for (int g = 0; g < 4; g++) 267 | { 268 | if (temperatures[g] < templow && temperatures[g] > -40) 269 | { 270 | templow = temperatures[g]; 271 | } 272 | } 273 | return (templow); 274 | */ 275 | if (temperatures[0] < temperatures[1] ) 276 | { 277 | return (temperatures[0]); 278 | } 279 | else 280 | { 281 | return (temperatures[1]); 282 | } 283 | } 284 | 285 | float BMSModule::getHighTemp() 286 | { 287 | /* 288 | float temphigh = -39; 289 | for (int g = 0; g < 4; g++) 290 | { 291 | if (temperatures[g] > temphigh && temperatures[g] > -40) 292 | { 293 | temphigh = temperatures[g]; 294 | } 295 | } 296 | return (temphigh); 297 | */ 298 | 299 | if (temperatures[0] > temperatures[1]) 300 | { 301 | return (temperatures[0]); 302 | } 303 | else 304 | { 305 | return (temperatures[1]); 306 | } 307 | } 308 | 309 | float BMSModule::getAvgTemp() 310 | { 311 | float avgtemp = 0; 312 | int num = 0; 313 | for (int g = 0; g < 2; g++) 314 | { 315 | if (temperatures[g] > -40) 316 | { 317 | avgtemp = avgtemp + temperatures[g]; 318 | num++; 319 | } 320 | } 321 | avgtemp = avgtemp / (float)(num); 322 | return (avgtemp); 323 | } 324 | 325 | float BMSModule::getModuleVoltage() 326 | { 327 | moduleVolt = 0; 328 | for (int I; I < 16; I++) 329 | { 330 | if (cellVolt[I] > IgnoreCell && cellVolt[I] < 5.0) 331 | { 332 | moduleVolt = moduleVolt + cellVolt[I]; 333 | } 334 | } 335 | return moduleVolt; 336 | } 337 | 338 | float BMSModule::getTemperature(int temp) 339 | { 340 | if (temp < 0 || temp > 3) return 0.0f; 341 | return temperatures[temp]; 342 | } 343 | 344 | void BMSModule::setAddress(int newAddr) 345 | { 346 | if (newAddr < 0 || newAddr > MAX_MODULE_ADDR) return; 347 | moduleAddress = newAddr; 348 | } 349 | 350 | int BMSModule::getAddress() 351 | { 352 | return moduleAddress; 353 | } 354 | 355 | uint32_t BMSModule::getError() 356 | { 357 | return error; 358 | } 359 | 360 | bool BMSModule::isExisting() 361 | { 362 | return exists; 363 | } 364 | 365 | bool BMSModule::isReset() 366 | { 367 | return reset; 368 | } 369 | 370 | 371 | void BMSModule::settempsensor(int tempsensor) 372 | { 373 | sensor = tempsensor; 374 | } 375 | 376 | void BMSModule::setExists(bool ex) 377 | { 378 | exists = ex; 379 | } 380 | 381 | void BMSModule::setReset(bool ex) 382 | { 383 | reset = ex; 384 | } 385 | 386 | void BMSModule::setIgnoreCell(float Ignore) 387 | { 388 | IgnoreCell = Ignore; 389 | /* 390 | Serial.println(); 391 | Serial.println(); 392 | Serial.println(Ignore); 393 | Serial.println(); 394 | */ 395 | } 396 | 397 | void BMSModule::setTempOff( int16_t tempoff) 398 | { 399 | TempOff = tempoff; 400 | } 401 | -------------------------------------------------------------------------------- /BMSModuleManager.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "BMSModuleManager.h" 3 | #include "BMSUtil.h" 4 | #include "Logger.h" 5 | 6 | extern EEPROMSettings settings; 7 | 8 | BMSModuleManager::BMSModuleManager() 9 | { 10 | for (int i = 1; i <= MAX_MODULE_ADDR; i++) { 11 | modules[i].setExists(false); 12 | modules[i].setAddress(i); 13 | } 14 | lowestPackVolt = 1000.0f; 15 | highestPackVolt = 0.0f; 16 | lowestPackTemp = 200.0f; 17 | highestPackTemp = -100.0f; 18 | isFaulted = false; 19 | } 20 | 21 | bool BMSModuleManager::checkcomms() 22 | { 23 | int g = 0; 24 | for (int y = 1; y < 63; y++) 25 | { 26 | if (modules[y].isExisting()) 27 | { 28 | g = 1; 29 | if (modules[y].isReset()) 30 | { 31 | //Do nothing as the counter has been reset 32 | } 33 | else 34 | { 35 | return false; 36 | } 37 | } 38 | modules[y].setReset(false); 39 | } 40 | if ( g == 0) 41 | { 42 | return false; 43 | } 44 | return true; 45 | } 46 | 47 | 48 | bool BMSModuleManager::checkstatus() 49 | { 50 | for (int y = 1; y < 20; y++) 51 | { 52 | if (modules[y].isExisting()) 53 | { 54 | if (modules[y].getError() & 0x2000 >= 0) 55 | { 56 | return true; 57 | } 58 | } 59 | } 60 | return false; 61 | } 62 | 63 | int BMSModuleManager::seriescells() 64 | { 65 | spack = 0; 66 | for (int y = 1; y < 63; y++) 67 | { 68 | if (modules[y].isExisting()) 69 | { 70 | spack = spack + modules[y].getscells(); 71 | } 72 | } 73 | return spack; 74 | } 75 | 76 | void BMSModuleManager::clearmodules() 77 | { 78 | for (int y = 1; y < 63; y++) 79 | { 80 | if (modules[y].isExisting()) 81 | { 82 | modules[y].clearmodule(); 83 | modules[y].setExists(false); 84 | modules[y].setAddress(y); 85 | } 86 | } 87 | } 88 | 89 | int BMSModuleManager::getNumModules() 90 | { 91 | return numFoundModules; 92 | } 93 | 94 | void BMSModuleManager::decodetemp(CAN_message_t &msg, int debug, int CSC) 95 | { 96 | int CMU = (msg.id & 0x00F) + 1; 97 | modules[CMU].decodetemp(msg, CSC); 98 | if (debug == 1 && CMU > 0) 99 | { 100 | Serial.println(); 101 | Serial.print(CMU); 102 | Serial.print(" Temp Found"); 103 | } 104 | } 105 | 106 | void BMSModuleManager::decodecan(CAN_message_t &msg, int debug) 107 | { 108 | int Id = (msg.id & 0x0F0); 109 | int CMU = (msg.id & 0x00F) + 1; 110 | /* 111 | if (msg.id == 0x100) 112 | { 113 | Serial.println(msg.id, HEX); 114 | } 115 | */ 116 | switch (Id) 117 | { 118 | case 0x000: 119 | Id = 0; 120 | break; 121 | case 0x020: 122 | Id = 1; 123 | break; 124 | case 0x030: 125 | Id = 2; 126 | break; 127 | 128 | case 0x040: 129 | Id = 3; 130 | break; 131 | 132 | case 0x050: 133 | Id = 4; 134 | break; 135 | 136 | case 0x060: 137 | Id = 5; 138 | break; 139 | 140 | case 0x070: 141 | Id = 6; 142 | break; 143 | } 144 | if (CMU < 14 && Id < 7) 145 | { 146 | if (debug == 1) 147 | { 148 | Serial.print(CMU); 149 | Serial.print(","); 150 | Serial.print(Id); 151 | Serial.println(); 152 | } 153 | } 154 | modules[CMU].setExists(true); 155 | modules[CMU].setReset(true); 156 | modules[CMU].decodecan(Id, msg, BalIgnore); 157 | } 158 | 159 | void BMSModuleManager::getAllVoltTemp() 160 | { 161 | packVolt = 0.0f; 162 | for (int x = 1; x <= MAX_MODULE_ADDR; x++) 163 | { 164 | if (modules[x].isExisting()) 165 | { 166 | Logger::debug(""); 167 | Logger::debug("Module %i exists. Reading voltage and temperature values", x); 168 | Logger::debug("Module voltage: %f", modules[x].getModuleVoltage()); 169 | Logger::debug("Lowest Cell V: %f Highest Cell V: %f", modules[x].getLowCellV(), modules[x].getHighCellV()); 170 | Logger::debug("Temp1: %f Temp2: %f", modules[x].getTemperature(0), modules[x].getTemperature(1)); 171 | packVolt += modules[x].getModuleVoltage(); 172 | if (modules[x].getLowTemp() < lowestPackTemp) lowestPackTemp = modules[x].getLowTemp(); 173 | if (modules[x].getHighTemp() > highestPackTemp) highestPackTemp = modules[x].getHighTemp(); 174 | } 175 | } 176 | 177 | packVolt = packVolt / Pstring; 178 | if (packVolt > highestPackVolt) highestPackVolt = packVolt; 179 | if (packVolt < lowestPackVolt) lowestPackVolt = packVolt; 180 | 181 | if (digitalRead(11) == LOW) { 182 | if (!isFaulted) Logger::error("One or more BMS modules have entered the fault state!"); 183 | isFaulted = true; 184 | } 185 | else 186 | { 187 | if (isFaulted) Logger::info("All modules have exited a faulted state"); 188 | isFaulted = false; 189 | } 190 | } 191 | 192 | float BMSModuleManager::getLowCellVolt() 193 | { 194 | LowCellVolt = 5.0; 195 | for (int x = 1; x <= MAX_MODULE_ADDR; x++) 196 | { 197 | if (modules[x].isExisting()) 198 | { 199 | if (modules[x].getLowCellV() < LowCellVolt) LowCellVolt = modules[x].getLowCellV(); 200 | } 201 | } 202 | return LowCellVolt; 203 | } 204 | 205 | float BMSModuleManager::getHighCellVolt() 206 | { 207 | HighCellVolt = 0.0; 208 | for (int x = 1; x <= MAX_MODULE_ADDR; x++) 209 | { 210 | if (modules[x].isExisting()) 211 | { 212 | if (modules[x].getHighCellV() > HighCellVolt) HighCellVolt = modules[x].getHighCellV(); 213 | } 214 | } 215 | return HighCellVolt; 216 | } 217 | 218 | float BMSModuleManager::getPackVoltage() 219 | { 220 | return packVolt; 221 | } 222 | 223 | float BMSModuleManager::getLowVoltage() 224 | { 225 | return lowestPackVolt; 226 | } 227 | 228 | float BMSModuleManager::getHighVoltage() 229 | { 230 | return highestPackVolt; 231 | } 232 | 233 | void BMSModuleManager::setBatteryID(int id) 234 | { 235 | batteryID = id; 236 | } 237 | 238 | void BMSModuleManager::setBalIgnore(bool BalIgn) 239 | { 240 | BalIgnore = BalIgn; 241 | } 242 | 243 | void BMSModuleManager::setPstrings(int Pstrings) 244 | { 245 | Pstring = Pstrings; 246 | } 247 | 248 | void BMSModuleManager::setSensors(int sensor, float Ignore, int tempoff) 249 | { 250 | for (int x = 1; x <= MAX_MODULE_ADDR; x++) 251 | { 252 | if (modules[x].isExisting()) 253 | { 254 | modules[x].settempsensor(sensor); 255 | modules[x].setIgnoreCell(Ignore); 256 | modules[x].setTempOff(tempoff); 257 | } 258 | } 259 | } 260 | 261 | float BMSModuleManager::getAvgTemperature() 262 | { 263 | float avg = 0.0f; 264 | lowTemp = 999.0f; 265 | highTemp = -999.0f; 266 | int y = 0; //counter for modules below -70 (no sensors connected) 267 | numFoundModules = 0; 268 | for (int x = 1; x <= MAX_MODULE_ADDR; x++) 269 | { 270 | if (modules[x].isExisting()) 271 | { 272 | numFoundModules++; 273 | if (modules[x].getAvgTemp() > -70) 274 | { 275 | avg += modules[x].getAvgTemp(); 276 | if (modules[x].getHighTemp() > highTemp) 277 | { 278 | highTemp = modules[x].getHighTemp(); 279 | } 280 | if (modules[x].getLowTemp() < lowTemp) 281 | { 282 | lowTemp = modules[x].getLowTemp(); 283 | 284 | } 285 | } 286 | else 287 | { 288 | y++; 289 | } 290 | } 291 | } 292 | avg = avg / (float)(numFoundModules - y); 293 | 294 | return avg; 295 | } 296 | 297 | float BMSModuleManager::getHighTemperature() 298 | { 299 | return highTemp; 300 | } 301 | 302 | float BMSModuleManager::getLowTemperature() 303 | { 304 | return lowTemp; 305 | } 306 | 307 | float BMSModuleManager::getAvgCellVolt() 308 | { 309 | float avg = 0.0f; 310 | for (int x = 1; x <= MAX_MODULE_ADDR; x++) 311 | { 312 | if (modules[x].isExisting()) avg += modules[x].getAverageV(); 313 | } 314 | avg = avg / (float)numFoundModules; 315 | 316 | return avg; 317 | } 318 | 319 | void BMSModuleManager::printPackSummary() 320 | { 321 | uint8_t faults; 322 | uint8_t alerts; 323 | uint8_t COV; 324 | uint8_t CUV; 325 | 326 | Logger::console(""); 327 | Logger::console(""); 328 | Logger::console(""); 329 | Logger::console("Modules: %i Cells: %i Voltage: %fV Avg Cell Voltage: %fV Avg Temp: %fC ", numFoundModules, seriescells(), 330 | getPackVoltage(), getAvgCellVolt(), getAvgTemperature()); 331 | Logger::console(""); 332 | for (int y = 1; y < 63; y++) 333 | { 334 | if (modules[y].isExisting()) 335 | { 336 | faults = modules[y].getFaults(); 337 | alerts = modules[y].getAlerts(); 338 | COV = modules[y].getCOVCells(); 339 | CUV = modules[y].getCUVCells(); 340 | 341 | Logger::console(" Module #%i", y); 342 | 343 | Logger::console(" Voltage: %fV (%fV-%fV) Temperatures: (%fC-%fC)", modules[y].getModuleVoltage(), 344 | modules[y].getLowCellV(), modules[y].getHighCellV(), modules[y].getLowTemp(), modules[y].getHighTemp()); 345 | if (faults > 0) 346 | { 347 | Logger::console(" MODULE IS FAULTED:"); 348 | if (faults & 1) 349 | { 350 | SERIALCONSOLE.print(" Overvoltage Cell Numbers (1-6): "); 351 | for (int i = 0; i < 12; i++) 352 | { 353 | if (COV & (1 << i)) 354 | { 355 | SERIALCONSOLE.print(i + 1); 356 | SERIALCONSOLE.print(" "); 357 | } 358 | } 359 | SERIALCONSOLE.println(); 360 | } 361 | if (faults & 2) 362 | { 363 | SERIALCONSOLE.print(" Undervoltage Cell Numbers (1-6): "); 364 | for (int i = 0; i < 12; i++) 365 | { 366 | if (CUV & (1 << i)) 367 | { 368 | SERIALCONSOLE.print(i + 1); 369 | SERIALCONSOLE.print(" "); 370 | } 371 | } 372 | SERIALCONSOLE.println(); 373 | } 374 | if (faults & 4) 375 | { 376 | Logger::console(" CRC error in received packet"); 377 | } 378 | if (faults & 8) 379 | { 380 | Logger::console(" Power on reset has occurred"); 381 | } 382 | if (faults & 0x10) 383 | { 384 | Logger::console(" Test fault active"); 385 | } 386 | if (faults & 0x20) 387 | { 388 | Logger::console(" Internal registers inconsistent"); 389 | } 390 | } 391 | if (alerts > 0) 392 | { 393 | Logger::console(" MODULE HAS ALERTS:"); 394 | if (alerts & 1) 395 | { 396 | Logger::console(" Over temperature on TS1"); 397 | } 398 | if (alerts & 2) 399 | { 400 | Logger::console(" Over temperature on TS2"); 401 | } 402 | if (alerts & 4) 403 | { 404 | Logger::console(" Sleep mode active"); 405 | } 406 | if (alerts & 8) 407 | { 408 | Logger::console(" Thermal shutdown active"); 409 | } 410 | if (alerts & 0x10) 411 | { 412 | Logger::console(" Test Alert"); 413 | } 414 | if (alerts & 0x20) 415 | { 416 | Logger::console(" OTP EPROM Uncorrectable Error"); 417 | } 418 | if (alerts & 0x40) 419 | { 420 | Logger::console(" GROUP3 Regs Invalid"); 421 | } 422 | if (alerts & 0x80) 423 | { 424 | Logger::console(" Address not registered"); 425 | } 426 | } 427 | if (faults > 0 || alerts > 0) SERIALCONSOLE.println(); 428 | } 429 | } 430 | } 431 | 432 | void BMSModuleManager::printPackDetails(int digits, int CSCvariant) 433 | { 434 | uint8_t faults; 435 | uint8_t alerts; 436 | uint8_t COV; 437 | uint8_t CUV; 438 | int cellNum = 0; 439 | 440 | Logger::console(""); 441 | Logger::console(""); 442 | Logger::console(""); 443 | Logger::console("Modules: %i Cells: %i Strings: %i Voltage: %fV Avg Cell Voltage: %fV Low Cell Voltage: %fV High Cell Voltage: %fV Delta Voltage: %zmV Avg Temp: %fC ", numFoundModules, seriescells(), 444 | Pstring, getPackVoltage(), getAvgCellVolt(), LowCellVolt, HighCellVolt, (HighCellVolt - LowCellVolt) * 1000, getAvgTemperature()); 445 | Logger::console(""); 446 | for (int y = 1; y < 63; y++) 447 | { 448 | if (modules[y].isExisting()) 449 | { 450 | faults = modules[y].getFaults(); 451 | alerts = modules[y].getAlerts(); 452 | COV = modules[y].getCOVCells(); 453 | CUV = modules[y].getCUVCells(); 454 | 455 | SERIALCONSOLE.print("Module #"); 456 | SERIALCONSOLE.print(y); 457 | if (y < 10) SERIALCONSOLE.print(" "); 458 | SERIALCONSOLE.print(" "); 459 | SERIALCONSOLE.print(modules[y].getModuleVoltage(), digits); 460 | SERIALCONSOLE.print("V"); 461 | for (int i = 0; i < 16; i++) 462 | { 463 | if (cellNum < 10) SERIALCONSOLE.print(" "); 464 | SERIALCONSOLE.print(" Cell"); 465 | SERIALCONSOLE.print(cellNum++); 466 | SERIALCONSOLE.print(": "); 467 | SERIALCONSOLE.print(modules[y].getCellVoltage(i), digits); 468 | SERIALCONSOLE.print("V"); 469 | } 470 | /* 471 | if (CSCvariant == 0) 472 | { 473 | for (int i = 0; i < 12; i++) 474 | { 475 | if (cellNum < 10) SERIALCONSOLE.print(" "); 476 | SERIALCONSOLE.print(" Cell"); 477 | SERIALCONSOLE.print(cellNum++); 478 | SERIALCONSOLE.print(": "); 479 | SERIALCONSOLE.print(modules[y].getCellVoltage(i), digits); 480 | SERIALCONSOLE.print("V"); 481 | } 482 | } 483 | if (CSCvariant == 1) 484 | { 485 | for (int i = 0; i < 8; i++) 486 | { 487 | if (cellNum < 10) SERIALCONSOLE.print(" "); 488 | SERIALCONSOLE.print(" Cell"); 489 | SERIALCONSOLE.print(cellNum++); 490 | SERIALCONSOLE.print(": "); 491 | SERIALCONSOLE.print(modules[y].getCellVoltage(i), digits); 492 | SERIALCONSOLE.print("V"); 493 | } 494 | } 495 | */ 496 | SERIALCONSOLE.println(); 497 | 498 | SERIALCONSOLE.print(" Temp 1: "); 499 | SERIALCONSOLE.print(modules[y].getTemperature(0)); 500 | 501 | SERIALCONSOLE.print("C Temp 2: "); 502 | SERIALCONSOLE.print(modules[y].getTemperature(1)); 503 | SERIALCONSOLE.print("C Status: 0x"); 504 | SERIALCONSOLE.print(modules[y].getError(), HEX); 505 | SERIALCONSOLE.print(" Bal: 0x"); 506 | SERIALCONSOLE.println(modules[y].getbalstat(), HEX); 507 | 508 | } 509 | } 510 | } 511 | 512 | void BMSModuleManager::printAllCSV(unsigned long timestamp, float current, int SOC) 513 | { 514 | for (int y = 1; y < 63; y++) 515 | { 516 | if (modules[y].isExisting()) 517 | { 518 | SERIALCONSOLE.print(timestamp); 519 | SERIALCONSOLE.print(","); 520 | SERIALCONSOLE.print(current, 0); 521 | SERIALCONSOLE.print(","); 522 | SERIALCONSOLE.print(SOC); 523 | SERIALCONSOLE.print(","); 524 | SERIALCONSOLE.print(y); 525 | SERIALCONSOLE.print(","); 526 | for (int i = 0; i < 8; i++) 527 | { 528 | SERIALCONSOLE.print(modules[y].getCellVoltage(i)); 529 | SERIALCONSOLE.print(","); 530 | } 531 | SERIALCONSOLE.print(modules[y].getTemperature(0)); 532 | SERIALCONSOLE.print(","); 533 | SERIALCONSOLE.print(modules[y].getTemperature(1)); 534 | SERIALCONSOLE.print(","); 535 | SERIALCONSOLE.print(modules[y].getTemperature(2)); 536 | SERIALCONSOLE.println(); 537 | } 538 | } 539 | for (int y = 1; y < 63; y++) 540 | { 541 | if (modules[y].isExisting()) 542 | { 543 | Serial2.print(timestamp); 544 | Serial2.print(","); 545 | Serial2.print(current, 0); 546 | Serial2.print(","); 547 | Serial2.print(SOC); 548 | Serial2.print(","); 549 | Serial2.print(y); 550 | Serial2.print(","); 551 | for (int i = 0; i < 8; i++) 552 | { 553 | Serial2.print(modules[y].getCellVoltage(i)); 554 | Serial2.print(","); 555 | } 556 | Serial2.print(modules[y].getTemperature(0)); 557 | Serial2.print(","); 558 | Serial2.print(modules[y].getTemperature(1)); 559 | Serial2.print(","); 560 | Serial2.print(modules[y].getTemperature(2)); 561 | Serial2.println(); 562 | } 563 | } 564 | } 565 | -------------------------------------------------------------------------------- /BMWPhevBMS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019 Simp ECO Engineering 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 17 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 18 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 19 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | Thank you to James Warner for proving out canbus decoding and finding balancing bits 22 | */ 23 | 24 | #include "BMSModuleManager.h" 25 | #include 26 | #include "config.h" 27 | #include "SerialConsole.h" 28 | #include "Logger.h" 29 | #include "CRC8.h" 30 | #include //https://github.com/pedvide/ADC 31 | #include 32 | #include //https://github.com/collin80/FlexCAN_Library 33 | #include 34 | #include //https://github.com/JonHub/Filters 35 | 36 | #define RESTART_ADDR 0xE000ED0C 37 | #define READ_RESTART() (*(volatile uint32_t *)RESTART_ADDR) 38 | #define WRITE_RESTART(val) ((*(volatile uint32_t *)RESTART_ADDR) = (val)) 39 | #define CPU_REBOOT WRITE_RESTART(0x5FA0004) 40 | 41 | BMSModuleManager bms; 42 | SerialConsole console; 43 | EEPROMSettings settings; 44 | 45 | /////Version Identifier///////// 46 | int firmver = 260424; 47 | 48 | //Curent filter// 49 | float filterFrequency = 5.0; 50 | FilterOnePole lowpassFilter(LOWPASS, filterFrequency); 51 | 52 | //Simple BMS V2 wiring// 53 | const int ACUR2 = A0; // current 1 54 | const int ACUR1 = A1; // current 2 55 | const int IN1 = 17; // input 1 - high active 56 | const int IN2 = 16; // input 2- high active 57 | const int IN3 = 18; // input 1 - high active 58 | const int IN4 = 19; // input 2- high active 59 | const int OUT1 = 11; // output 1 - high active 60 | const int OUT2 = 12; // output 2 - high active 61 | const int OUT3 = 20; // output 3 - high active 62 | const int OUT4 = 21; // output 4 - high active 63 | const int OUT5 = 22; // output 5 - Low active 64 | const int OUT6 = 23; // output 6 - Low active 65 | const int OUT7 = 5; // output 7 - Low active 66 | const int OUT8 = 6; // output 8 - Low active 67 | const int led = 13; 68 | const int BMBfault = 11; 69 | 70 | byte bmsstatus = 0; 71 | //bms status values 72 | #define Boot 0 73 | #define Ready 1 74 | #define Drive 2 75 | #define Charge 3 76 | #define Precharge 4 77 | #define Error 5 78 | // 79 | //Current sensor values 80 | #define Undefined 0 81 | #define Analoguedual 1 82 | #define Canbus 2 83 | #define Analoguesing 3 84 | 85 | // Can current sensor values 86 | #define LemCAB300 1 87 | #define IsaScale 3 88 | #define VictronLynx 4 89 | #define LemCAB500 2 90 | #define CurCanMax 4 // max value 91 | 92 | 93 | // 94 | //Charger Types 95 | #define NoCharger 0 96 | #define BrusaNLG5 1 97 | #define ChevyVolt 2 98 | #define Eltek 3 99 | #define Elcon 4 100 | #define HVSBS 5 101 | // 102 | 103 | //CSC Variants 104 | #define BmwI3 0 105 | #define MiniE 1 106 | // 107 | 108 | 109 | int Discharge; 110 | int ErrorReason = 0; 111 | 112 | //variables for output control 113 | int pulltime = 100; 114 | int contctrl, contstat = 0; //1 = out 5 high 2 = out 6 high 3 = both high 115 | unsigned long conttimer1, conttimer2, conttimer3, Pretimer, Pretimer1, overtriptimer, undertriptimer, mainconttimer, balancetimer = 0; 116 | uint16_t pwmfreq = 18000; //pwm frequency 117 | 118 | int pwmcurmax = 50; //Max current to be shown with pwm 119 | int pwmcurmid = 50; //Mid point for pwm dutycycle based on current 120 | int16_t pwmcurmin = 0; //DONOT fill in, calculated later based on other values 121 | 122 | 123 | //variables for VE driect bus comms 124 | char *myStrings[] = { "V", "14674", "I", "0", "CE", "-1", "SOC", "800", "TTG", "-1", "Alarm", "OFF", "Relay", "OFF", "AR", "0", "BMV", "600S", "FW", "212", "H1", "-3", "H2", "-3", "H3", "0", "H4", "0", "H5", "0", "H6", "-7", "H7", "13180", "H8", "14774", "H9", "137", "H10", "0", "H11", "0", "H12", "0" }; 125 | 126 | //variables for VE can 127 | uint16_t chargevoltage = 49100; //max charge voltage in mv 128 | uint16_t chargecurrent, tempchargecurrent = 0; 129 | uint16_t disvoltage = 42000; // max discharge voltage in mv 130 | int discurrent = 0; 131 | int batvcal = 0; 132 | 133 | uint16_t SOH = 100; // SOH place holder 134 | 135 | unsigned char alarm[4] = { 0, 0, 0, 0 }; 136 | unsigned char warning[4] = { 0, 0, 0, 0 }; 137 | unsigned char mes[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 138 | unsigned char bmsname[8] = { 'S', 'I', 'M', 'P', ' ', 'B', 'M', 'S' }; 139 | unsigned char bmsmanu[8] = { 'T', 'O', 'M', ' ', 'D', 'E', ' ', 'B' }; 140 | long unsigned int rxId; 141 | unsigned char len = 0; 142 | byte rxBuf[8]; 143 | char msgString[128]; // Array to store serial string 144 | uint32_t inbox; 145 | signed long CANmilliamps; 146 | signed long voltage1, voltage2, voltage3 = 0; //mV only with ISAscale sensor 147 | 148 | //struct can_frame canMsg; 149 | //MCP2515 CAN1(10); //set CS pin for can controlelr 150 | 151 | 152 | //variables for current calulation 153 | int value; 154 | float currentact, RawCur; 155 | float ampsecond; 156 | unsigned long lasttime; 157 | unsigned long looptime, looptime1, UnderTime, OverTime, cleartime, baltimer, commandtime = 0; //ms 158 | int currentsense = 14; 159 | int sensor = 1; 160 | 161 | //Variables for SOC calc 162 | int SOC = 100; //State of Charge 163 | int SOCset = 0; 164 | int SOCtest = 0; 165 | int SOCmem = 0; 166 | int SOCreset = 0; 167 | 168 | ///charger variables 169 | int maxac1 = 16; //Shore power 16A per charger 170 | int maxac2 = 10; //Generator Charging 171 | int chargerid1 = 0x618; //bulk chargers 172 | int chargerid2 = 0x638; //finishing charger 173 | float chargerendbulk = 0; //V before Charge Voltage to turn off the bulk charger/s 174 | float chargerend = 0; //V before Charge Voltage to turn off the finishing charger/s 175 | int chargertoggle = 0; 176 | int ncharger = 1; // number of chargers 177 | bool chargecurrentlimit = 0; 178 | 179 | //AC current control 180 | volatile uint32_t pilottimer = 0; 181 | volatile uint16_t timehigh, duration = 0; 182 | volatile uint16_t accurlim = 0; 183 | volatile int dutycycle = 0; 184 | uint16_t chargerpower = 0; 185 | bool CPdebug = 0; 186 | 187 | //variables 188 | int outputstate = 0; 189 | int incomingByte = 0; 190 | int x = 0; 191 | int storagemode = 0; 192 | int cellspresent = 0; 193 | int dashused = 1; 194 | int Charged = 0; 195 | bool balancepauze = 0; 196 | 197 | 198 | //Debugging modes////////////////// 199 | int debug = 1; 200 | int inputcheck = 0; //read digital inputs 201 | int outputcheck = 0; //check outputs 202 | int candebug = 0; //view can frames 203 | int gaugedebug = 0; 204 | int debugCur = 0; 205 | int CSVdebug = 0; 206 | int menuload = 0; 207 | int balancecells; 208 | int debugdigits = 2; //amount of digits behind decimal for voltage reading 209 | int balancedebug = 0; 210 | 211 | //BMW Can Variables/// 212 | 213 | //uint8_t check1[8] = {0x13, 0x76, 0xD9, 0xBC, 0x9A, 0xFF, 0x50, 0x35}; 214 | //uint8_t check2[8] = {0x4A, 0x2F, 0x80, 0xE5, 0xC3, 0xA6, 0x09, 0x6C}; 215 | uint8_t Imod, mescycle = 0; 216 | uint8_t nextmes = 0; 217 | uint16_t commandrate = 50; 218 | uint8_t testcycle = 0; 219 | uint8_t DMC[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 220 | uint8_t Unassigned, NextID = 0; 221 | 222 | //BMW checksum variable/// 223 | 224 | CRC8 crc8; 225 | uint8_t checksum; 226 | const uint8_t finalxor[12] = { 0xCF, 0xF5, 0xBB, 0x81, 0x27, 0x1D, 0x53, 0x69, 0x02, 0x38, 0x76, 0x4C }; 227 | 228 | 229 | 230 | ADC *adc = new ADC(); // adc object 231 | 232 | void loadSettings() { 233 | Logger::console("Resetting to factory defaults"); 234 | settings.version = EEPROM_VERSION; 235 | settings.checksum = 2; 236 | settings.canSpeed = 500000; 237 | settings.batteryID = 0x01; //in the future should be 0xFF to force it to ask for an address 238 | settings.OverVSetpoint = 4.2f; 239 | settings.UnderVSetpoint = 3.0f; 240 | settings.ChargeVsetpoint = 4.1f; 241 | settings.ChargeHys = 0.2f; // voltage drop required for charger to kick back on 242 | settings.WarnOff = 0.1f; //voltage offset to raise a warning 243 | settings.DischVsetpoint = 3.2f; 244 | settings.DischHys = 0.2f; // Discharge voltage offset 245 | settings.CellGap = 0.2f; //max delta between high and low cell 246 | settings.OverTSetpoint = 65.0f; 247 | settings.UnderTSetpoint = -10.0f; 248 | settings.ChargeTSetpoint = 0.0f; 249 | settings.triptime = 500; //mS of delay before counting over or undervoltage 250 | settings.DisTSetpoint = 40.0f; 251 | settings.WarnToff = 5.0f; //temp offset before raising warning 252 | settings.IgnoreTemp = 0; // 0 - use both sensors, 1 or 2 only use that sensor 253 | settings.IgnoreVolt = 0.5; // 254 | settings.balanceVoltage = 3.9f; 255 | settings.balanceHyst = 0.04f; 256 | settings.balanceDuty = 60; 257 | settings.logLevel = 2; 258 | settings.CAP = 100; //battery size in Ah 259 | settings.Pstrings = 1; // strings in parallel used to divide voltage of pack 260 | settings.Scells = 12; //Cells in series 261 | settings.StoreVsetpoint = 3.8; // V storage mode charge max 262 | settings.discurrentmax = 300; // max discharge current in 0.1A 263 | settings.DisTaper = 0.3f; //V offset to bring in discharge taper to Zero Amps at settings.DischVsetpoint 264 | settings.chargecurrentmax = 300; //max charge current in 0.1A 265 | settings.chargecurrent2max = 150; //max charge current in 0.1A 266 | settings.chargecurrentend = 50; //end charge current in 0.1A 267 | settings.socvolt[0] = 3100; //Voltage and SOC curve for voltage based SOC calc 268 | settings.socvolt[1] = 10; //Voltage and SOC curve for voltage based SOC calc 269 | settings.socvolt[2] = 4100; //Voltage and SOC curve for voltage based SOC calc 270 | settings.socvolt[3] = 90; //Voltage and SOC curve for voltage based SOC calc 271 | settings.invertcur = 0; //Invert current sensor direction 272 | settings.cursens = 2; 273 | settings.curcan = LemCAB300; 274 | settings.voltsoc = 0; //SOC purely voltage based 275 | settings.Pretime = 5000; //ms of precharge time 276 | settings.conthold = 50; //holding duty cycle for contactor 0-255 277 | settings.Precurrent = 1000; //ma before closing main contator 278 | settings.convhigh = 58; // mV/A current sensor high range channel 279 | settings.convlow = 643; // mV/A current sensor low range channel 280 | settings.offset1 = 1750; //mV mid point of channel 1 281 | settings.offset2 = 1750; //mV mid point of channel 2 282 | settings.changecur = 20000; //mA change overpoint 283 | settings.gaugelow = 50; //empty fuel gauge pwm 284 | settings.gaugehigh = 255; //full fuel gauge pwm 285 | settings.ESSmode = 0; //activate ESS mode 286 | settings.ncur = 1; //number of multiples to use for current measurement 287 | settings.chargertype = 2; // 1 - Brusa NLG5xx 2 - Volt charger 0 -No Charger 288 | settings.chargerspd = 100; //ms per message 289 | settings.chargereff = 85; //% effiecency of charger 290 | settings.chargerACv = 240; // AC input voltage into Charger 291 | settings.UnderDur = 5000; //ms of allowed undervoltage before throwing open stopping discharge. 292 | settings.CurDead = 5; // mV of dead band on current sensor 293 | settings.ChargerDirect = 1; //1 - charger is always connected to HV battery // 0 - Charger is behind the contactors 294 | settings.tripcont = 1; //in ESSmode 1 - Main contactor function, 0 - Trip function 295 | settings.CSCvariant = 0; //0 BMW I3 - 1 Mini-E 296 | settings.TempOff = 0; //Temperature offset 297 | } 298 | 299 | 300 | CAN_message_t msg; 301 | CAN_message_t inMsg; 302 | CAN_filter_t filter; 303 | 304 | uint32_t lastUpdate; 305 | 306 | 307 | void setup() { 308 | //delay(4000); //just for easy debugging. It takes a few seconds for USB to come up properly on most OS's 309 | //pinMode(ACUR1, INPUT);//Not required for Analogue Pins 310 | //pinMode(ACUR2, INPUT);//Not required for Analogue Pins 311 | pinMode(IN1, INPUT); 312 | pinMode(IN2, INPUT); 313 | pinMode(IN3, INPUT); 314 | pinMode(IN4, INPUT); 315 | pinMode(OUT1, OUTPUT); // drive contactor 316 | pinMode(OUT2, OUTPUT); // precharge 317 | pinMode(OUT3, OUTPUT); // charge relay 318 | pinMode(OUT4, OUTPUT); // Negative contactor 319 | pinMode(OUT5, OUTPUT); // pwm driver output 320 | pinMode(OUT6, OUTPUT); // pwm driver output 321 | pinMode(OUT7, OUTPUT); // pwm driver output 322 | pinMode(OUT8, OUTPUT); // pwm driver output 323 | pinMode(led, OUTPUT); 324 | 325 | analogWriteFrequency(OUT5, pwmfreq); 326 | analogWriteFrequency(OUT6, pwmfreq); 327 | analogWriteFrequency(OUT7, pwmfreq); 328 | analogWriteFrequency(OUT8, pwmfreq); 329 | 330 | Can0.begin(500000); 331 | 332 | //set filters for standard 333 | for (int i = 0; i < 8; i++) { 334 | Can0.getFilter(filter, i); 335 | filter.flags.extended = 0; 336 | Can0.setFilter(filter, i); 337 | } 338 | //set filters for extended 339 | for (int i = 9; i < 13; i++) { 340 | Can0.getFilter(filter, i); 341 | filter.flags.extended = 1; 342 | Can0.setFilter(filter, i); 343 | } 344 | 345 | //if using enable pins on a transceiver they need to be set on 346 | 347 | 348 | adc->adc0->setAveraging(16); // set number of averages 349 | adc->adc0->setResolution(16); // set bits of resolution 350 | adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); 351 | adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); 352 | adc->adc0->startContinuous(ACUR1); 353 | 354 | 355 | SERIALCONSOLE.begin(115200); 356 | SERIALCONSOLE.println("Starting up!"); 357 | SERIALCONSOLE.println("SimpBMS V2 BMW I3"); 358 | 359 | Serial2.begin(115200); 360 | 361 | // Display reason the Teensy was last reset 362 | Serial.println(); 363 | Serial.println("Reason for last Reset: "); 364 | 365 | if (RCM_SRS1 & RCM_SRS1_SACKERR) Serial.println("Stop Mode Acknowledge Error Reset"); 366 | if (RCM_SRS1 & RCM_SRS1_MDM_AP) Serial.println("MDM-AP Reset"); 367 | if (RCM_SRS1 & RCM_SRS1_SW) Serial.println("Software Reset"); // reboot with SCB_AIRCR = 0x05FA0004 368 | if (RCM_SRS1 & RCM_SRS1_LOCKUP) Serial.println("Core Lockup Event Reset"); 369 | if (RCM_SRS0 & RCM_SRS0_POR) Serial.println("Power-on Reset"); // removed / applied power 370 | if (RCM_SRS0 & RCM_SRS0_PIN) Serial.println("External Pin Reset"); // Reboot with software download 371 | if (RCM_SRS0 & RCM_SRS0_WDOG) Serial.println("Watchdog(COP) Reset"); // WDT timed out 372 | if (RCM_SRS0 & RCM_SRS0_LOC) Serial.println("Loss of External Clock Reset"); 373 | if (RCM_SRS0 & RCM_SRS0_LOL) Serial.println("Loss of Lock in PLL Reset"); 374 | if (RCM_SRS0 & RCM_SRS0_LVD) Serial.println("Low-voltage Detect Reset"); 375 | Serial.println(); 376 | /////////////////// 377 | 378 | 379 | // enable WDT 380 | noInterrupts(); // don't allow interrupts while setting up WDOG 381 | WDOG_UNLOCK = WDOG_UNLOCK_SEQ1; // unlock access to WDOG registers 382 | WDOG_UNLOCK = WDOG_UNLOCK_SEQ2; 383 | delayMicroseconds(1); // Need to wait a bit.. 384 | 385 | WDOG_TOVALH = 0x1000; 386 | WDOG_TOVALL = 0x0000; 387 | WDOG_PRESC = 0; 388 | WDOG_STCTRLH |= WDOG_STCTRLH_ALLOWUPDATE | WDOG_STCTRLH_WDOGEN | WDOG_STCTRLH_WAITEN | WDOG_STCTRLH_STOPEN | WDOG_STCTRLH_CLKSRC; 389 | interrupts(); 390 | ///////////////// 391 | 392 | 393 | SERIALBMS.begin(612500); //Tesla serial bus 394 | //VE.begin(19200); //Victron VE direct bus 395 | #if defined(__arm__) && defined(__SAM3X8E__) 396 | serialSpecialInit(USART0, 612500); //required for Due based boards as the stock core files don't support 612500 baud. 397 | #endif 398 | 399 | SERIALCONSOLE.println("Started serial interface to BMS."); 400 | 401 | EEPROM.get(0, settings); 402 | if (settings.version != EEPROM_VERSION) { 403 | loadSettings(); 404 | } 405 | Logger::setLoglevel(Logger::Off); //Debug = 0, Info = 1, Warn = 2, Error = 3, Off = 4 406 | 407 | lastUpdate = 0; 408 | 409 | crc8.begin(); 410 | digitalWrite(led, HIGH); 411 | bms.setPstrings(settings.Pstrings); 412 | bms.setSensors(settings.IgnoreTemp, settings.IgnoreVolt, settings.TempOff); 413 | 414 | 415 | //SOC recovery// 416 | 417 | SOC = (EEPROM.read(1000)); 418 | if (settings.voltsoc == 1) { 419 | SOCmem = 0; 420 | } else { 421 | if (SOC > 100) { 422 | SOCmem = 0; 423 | } else { 424 | SOCmem = 1; 425 | } 426 | } 427 | 428 | SERIALCONSOLE.println("Recovery SOC: "); 429 | SERIALCONSOLE.print(SOC); 430 | 431 | ////Calculate fixed numbers 432 | pwmcurmin = (pwmcurmid / 50 * pwmcurmax * -1); 433 | //// 434 | 435 | //precharge timer kickers 436 | Pretimer = millis(); 437 | Pretimer1 = millis(); 438 | 439 | // setup interrupts 440 | //RISING/HIGH/CHANGE/LOW/FALLING 441 | attachInterrupt(IN4, isrCP, CHANGE); // attach BUTTON 1 interrupt handler [ pin# 7 ] 442 | 443 | PMC_LVDSC1 = PMC_LVDSC1_LVDV(1); // enable hi v 444 | PMC_LVDSC2 = PMC_LVDSC2_LVWIE | PMC_LVDSC2_LVWV(3); // 2.92-3.08v 445 | attachInterruptVector(IRQ_LOW_VOLTAGE, low_voltage_isr); 446 | NVIC_ENABLE_IRQ(IRQ_LOW_VOLTAGE); 447 | 448 | bmsstatus = Boot; 449 | } 450 | 451 | void loop() { 452 | while (Can0.available()) { 453 | canread(); 454 | } 455 | 456 | if (SERIALCONSOLE.available() > 0) { 457 | menu(); 458 | } 459 | 460 | if (outputcheck != 1) { 461 | contcon(); 462 | if (settings.ESSmode == 1) { 463 | if (bmsstatus != Error) { 464 | contctrl = contctrl | 4; //turn on negative contactor 465 | 466 | if (settings.tripcont != 0) { 467 | if (bms.getLowCellVolt() > settings.UnderVSetpoint && bms.getHighCellVolt() < settings.OverVSetpoint) { 468 | if (digitalRead(OUT2) == LOW && digitalRead(OUT4) == LOW) { 469 | mainconttimer = millis(); 470 | digitalWrite(OUT4, HIGH); //Precharge start 471 | Serial.println(); 472 | Serial.println("Precharge!!!"); 473 | Serial.println(mainconttimer); 474 | Serial.println(); 475 | } 476 | if (mainconttimer + settings.Pretime < millis() && digitalRead(OUT2) == LOW && abs(currentact) < settings.Precurrent) { 477 | digitalWrite(OUT2, HIGH); //turn on contactor 478 | Serial.println(); 479 | Serial.println("Main On!!!"); 480 | Serial.println(); 481 | mainconttimer = millis() + settings.Pretime; 482 | } 483 | if (mainconttimer + settings.Pretime + 1000 < millis()) { 484 | digitalWrite(OUT4, LOW); //ensure precharge is low 485 | } 486 | } else { 487 | digitalWrite(OUT4, LOW); //ensure precharge is low 488 | mainconttimer = 0; 489 | } 490 | } 491 | if (digitalRead(IN1) == LOW) //Key OFF 492 | { 493 | if (storagemode == 1) { 494 | storagemode = 0; 495 | } 496 | } else { 497 | if (storagemode == 0) { 498 | storagemode = 1; 499 | } 500 | } 501 | if (bms.getHighCellVolt() > settings.balanceVoltage && bms.getHighCellVolt() > bms.getLowCellVolt() + settings.balanceHyst) { 502 | balancecells = 1; 503 | } else { 504 | balancecells = 0; 505 | } 506 | 507 | //Pretimer + settings.Pretime > millis(); 508 | 509 | if (storagemode == 1) { 510 | if (bms.getHighCellVolt() > settings.StoreVsetpoint || chargecurrent == 0) { 511 | digitalWrite(OUT3, LOW); //turn off charger 512 | contctrl = contctrl & 253; 513 | Pretimer = millis(); 514 | Charged = 1; 515 | SOCcharged(2); 516 | } else { 517 | if (Charged == 1) { 518 | if (bms.getHighCellVolt() < (settings.StoreVsetpoint - settings.ChargeHys)) { 519 | Charged = 0; 520 | digitalWrite(OUT3, HIGH); //turn on charger 521 | if (Pretimer + settings.Pretime < millis()) { 522 | contctrl = contctrl | 2; 523 | Pretimer = 0; 524 | } 525 | } 526 | } else { 527 | digitalWrite(OUT3, HIGH); //turn on charger 528 | if (Pretimer + settings.Pretime < millis()) { 529 | contctrl = contctrl | 2; 530 | Pretimer = 0; 531 | } 532 | } 533 | } 534 | } else { 535 | if (bms.getHighCellVolt() > settings.OverVSetpoint || bms.getHighCellVolt() > settings.ChargeVsetpoint || chargecurrent == 0) { 536 | if ((millis() - overtriptimer) > settings.triptime) { 537 | digitalWrite(OUT3, LOW); //turn off charger 538 | contctrl = contctrl & 253; 539 | Pretimer = millis(); 540 | Charged = 1; 541 | SOCcharged(2); 542 | } 543 | } else { 544 | overtriptimer = millis(); 545 | if (Charged == 1) { 546 | if (bms.getHighCellVolt() < (settings.ChargeVsetpoint - settings.ChargeHys)) { 547 | Charged = 0; 548 | digitalWrite(OUT3, HIGH); //turn on charger 549 | if (Pretimer + settings.Pretime < millis()) { 550 | // Serial.println(); 551 | //Serial.print(Pretimer); 552 | contctrl = contctrl | 2; 553 | } 554 | } 555 | } else { 556 | digitalWrite(OUT3, HIGH); //turn on charger 557 | if (Pretimer + settings.Pretime < millis()) { 558 | // Serial.println(); 559 | //Serial.print(Pretimer); 560 | contctrl = contctrl | 2; 561 | } 562 | } 563 | } 564 | } 565 | if (bms.getLowCellVolt() < settings.UnderVSetpoint || bms.getLowCellVolt() < settings.DischVsetpoint) { 566 | if ((millis() - undertriptimer) > settings.triptime) { 567 | digitalWrite(OUT1, LOW); //turn off discharge 568 | contctrl = contctrl & 254; 569 | Pretimer1 = millis(); 570 | } 571 | } else { 572 | undertriptimer = millis(); 573 | if (bms.getLowCellVolt() > settings.DischVsetpoint + settings.DischHys) { 574 | digitalWrite(OUT1, HIGH); //turn on discharge 575 | if (Pretimer1 + settings.Pretime < millis()) { 576 | contctrl = contctrl | 1; 577 | } 578 | } 579 | } 580 | if (SOCset == 1) { 581 | if (settings.tripcont == 0) { 582 | if (bms.getLowCellVolt() < settings.UnderVSetpoint || bms.getHighCellVolt() > settings.OverVSetpoint || bms.getHighTemperature() > settings.OverTSetpoint) { 583 | digitalWrite(OUT2, HIGH); //trip breaker 584 | } else { 585 | digitalWrite(OUT2, LOW); //trip breaker 586 | } 587 | } else { 588 | if (bms.getLowCellVolt() < settings.UnderVSetpoint || bms.getHighCellVolt() > settings.OverVSetpoint || bms.getHighTemperature() > settings.OverTSetpoint) { 589 | digitalWrite(OUT2, LOW); //turn off contactor 590 | digitalWrite(OUT4, LOW); //ensure precharge is low 591 | } 592 | } 593 | } 594 | 595 | } else { 596 | /* 597 | digitalWrite(OUT2, HIGH);//trip breaker 598 | Discharge = 0; 599 | digitalWrite(OUT4, LOW); 600 | digitalWrite(OUT3, LOW);//turn off charger 601 | digitalWrite(OUT2, LOW); 602 | digitalWrite(OUT1, LOW);//turn off discharge 603 | contctrl = 0; //turn off out 5 and 6 604 | */ 605 | if (SOCset == 1) { 606 | if (settings.tripcont == 0) { 607 | if (bms.getLowCellVolt() < settings.UnderVSetpoint || bms.getHighCellVolt() > settings.OverVSetpoint || bms.getHighTemperature() > settings.OverTSetpoint) { 608 | digitalWrite(OUT2, HIGH); //trip breaker 609 | } else { 610 | digitalWrite(OUT2, LOW); //trip breaker 611 | } 612 | } else { 613 | if (bms.getLowCellVolt() < settings.UnderVSetpoint || bms.getHighCellVolt() > settings.OverVSetpoint || bms.getHighTemperature() > settings.OverTSetpoint) { 614 | digitalWrite(OUT2, LOW); //turn off contactor 615 | digitalWrite(OUT4, LOW); //ensure precharge is low 616 | } 617 | } 618 | if (bms.getLowCellVolt() > settings.UnderVSetpoint || bms.getHighCellVolt() < settings.OverVSetpoint || bms.getHighTemperature() < settings.OverTSetpoint) { 619 | bmsstatus = Boot; 620 | } 621 | } 622 | } 623 | 624 | //pwmcomms(); 625 | } else { 626 | switch (bmsstatus) { 627 | case (Boot): 628 | Discharge = 0; 629 | digitalWrite(OUT4, LOW); 630 | digitalWrite(OUT3, LOW); //turn off charger 631 | digitalWrite(OUT2, LOW); 632 | digitalWrite(OUT1, LOW); //turn off discharge 633 | contctrl = 0; 634 | bmsstatus = Ready; 635 | break; 636 | 637 | case (Ready): 638 | Discharge = 0; 639 | digitalWrite(OUT4, LOW); 640 | digitalWrite(OUT3, LOW); //turn off charger 641 | digitalWrite(OUT2, LOW); 642 | digitalWrite(OUT1, LOW); //turn off discharge 643 | contctrl = 0; //turn off out 5 and 6 644 | if (bms.getHighCellVolt() > settings.balanceVoltage && bms.getHighCellVolt() > bms.getLowCellVolt() + settings.balanceHyst) { 645 | //bms.balanceCells(); 646 | balancecells = 1; 647 | } else { 648 | balancecells = 0; 649 | } 650 | if (digitalRead(IN3) == HIGH && (bms.getHighCellVolt() < (settings.ChargeVsetpoint - settings.ChargeHys))) //detect AC present for charging and check not balancing 651 | { 652 | if (settings.ChargerDirect == 1) { 653 | bmsstatus = Charge; 654 | } else { 655 | bmsstatus = Precharge; 656 | Pretimer = millis(); 657 | } 658 | } 659 | if (digitalRead(IN1) == HIGH && bms.getLowCellVolt() > settings.DischVsetpoint) //detect Key ON 660 | { 661 | bmsstatus = Precharge; 662 | Pretimer = millis(); 663 | } 664 | 665 | break; 666 | 667 | case (Precharge): 668 | Discharge = 0; 669 | Prechargecon(); 670 | break; 671 | 672 | 673 | case (Drive): 674 | Discharge = 1; 675 | if (digitalRead(IN1) == LOW) //Key OFF 676 | { 677 | bmsstatus = Ready; 678 | } 679 | if (digitalRead(IN3) == HIGH && (bms.getHighCellVolt() < (settings.ChargeVsetpoint - settings.ChargeHys))) //detect AC present for charging and check not balancing 680 | { 681 | bmsstatus = Charge; 682 | } 683 | 684 | break; 685 | 686 | case (Charge): 687 | Discharge = 0; 688 | if (digitalRead(IN2) == HIGH) { 689 | chargecurrentlimit = true; 690 | } else { 691 | chargecurrentlimit = false; 692 | } 693 | digitalWrite(OUT3, HIGH); //enable charger 694 | if (bms.getHighCellVolt() > settings.balanceVoltage) { 695 | //bms.balanceCells(); 696 | balancecells = 1; 697 | } else { 698 | balancecells = 0; 699 | } 700 | if (bms.getHighCellVolt() > settings.ChargeVsetpoint) { 701 | if (bms.getAvgCellVolt() > (settings.ChargeVsetpoint - settings.ChargeHys)) { 702 | SOCcharged(2); 703 | } else { 704 | SOCcharged(1); 705 | } 706 | digitalWrite(OUT3, LOW); //turn off charger 707 | bmsstatus = Ready; 708 | } 709 | if (digitalRead(IN3) == LOW) //detect AC not present for charging 710 | { 711 | bmsstatus = Ready; 712 | } 713 | break; 714 | 715 | case (Error): 716 | Discharge = 0; 717 | digitalWrite(OUT4, LOW); 718 | digitalWrite(OUT3, LOW); //turn off charger 719 | digitalWrite(OUT2, LOW); 720 | digitalWrite(OUT1, LOW); //turn off discharge 721 | contctrl = 0; //turn off out 5 and 6 722 | 723 | if (bms.getLowCellVolt() > settings.UnderVSetpoint && bms.getHighCellVolt() < settings.OverVSetpoint && digitalRead(IN1) == LOW) //Key OFF 724 | { 725 | bmsstatus = Ready; 726 | } 727 | 728 | break; 729 | } 730 | } 731 | if (settings.cursens == Analoguedual || settings.cursens == Analoguesing) { 732 | getcurrent(); 733 | } 734 | } 735 | if (millis() - commandtime > commandrate) { 736 | commandtime = millis(); 737 | sendcommand(); 738 | } 739 | 740 | 741 | if (millis() - looptime > 500) { 742 | looptime = millis(); 743 | bms.getAllVoltTemp(); 744 | //UV check 745 | if (settings.ESSmode == 1) { 746 | if (SOCset != 0) { 747 | if (bms.getLowCellVolt() < settings.UnderVSetpoint || bms.getHighCellVolt() < settings.UnderVSetpoint) { 748 | if (debug != 0) { 749 | SERIALCONSOLE.println(" "); 750 | SERIALCONSOLE.print(" !!! Undervoltage Fault !!!"); 751 | SERIALCONSOLE.println(" "); 752 | } 753 | bmsstatus = Error; 754 | ErrorReason = 1; 755 | } 756 | } 757 | } else //In 'vehicle' mode 758 | { 759 | if (SOCset != 0) { 760 | if (bms.getLowCellVolt() < settings.UnderVSetpoint) { 761 | if (UnderTime < millis()) //check is last time not undervoltage is longer thatn UnderDur ago 762 | { 763 | bmsstatus = Error; 764 | } 765 | } else { 766 | UnderTime = millis() + settings.triptime; 767 | } 768 | 769 | if (bms.getHighCellVolt() < settings.UnderVSetpoint || bms.getHighTemperature() > settings.OverTSetpoint) { 770 | bmsstatus = Error; 771 | } 772 | 773 | if (bms.getHighCellVolt() > settings.OverVSetpoint) { 774 | if (OverTime < millis()) //check is last time not undervoltage is longer thatn UnderDur ago 775 | { 776 | bmsstatus = Error; 777 | } 778 | } else { 779 | OverTime = millis() + settings.triptime; 780 | } 781 | } 782 | } 783 | 784 | if (debug != 0) { 785 | printbmsstat(); 786 | bms.printPackDetails(debugdigits, settings.CSCvariant); 787 | } 788 | if (CSVdebug != 0) { 789 | bms.printAllCSV(millis(), currentact, SOC); 790 | } 791 | if (inputcheck != 0) { 792 | inputdebug(); 793 | } 794 | 795 | if (outputcheck != 0) { 796 | outputdebug(); 797 | } else { 798 | gaugeupdate(); 799 | } 800 | 801 | updateSOC(); 802 | currentlimit(); 803 | if (SOCset != 0) { 804 | alarmupdate(); 805 | } 806 | VEcan(); 807 | 808 | if (cellspresent == 0 && millis() > 3000) { 809 | cellspresent = bms.seriescells(); //set amount of connected cells, might need delay 810 | bms.setSensors(settings.IgnoreTemp, settings.IgnoreVolt, settings.TempOff); 811 | } else { 812 | if (cellspresent != bms.seriescells()) //detect a fault in cells detected 813 | { 814 | if (debug != 0) { 815 | SERIALCONSOLE.println(" "); 816 | SERIALCONSOLE.print(" !!! Series Cells Fault !!!"); 817 | SERIALCONSOLE.println(" "); 818 | } 819 | bmsstatus = Error; 820 | ErrorReason = 3; 821 | } 822 | } 823 | 824 | if (CSVdebug != 1) { 825 | dashupdate(); 826 | } 827 | 828 | ///stop reading voltages during balancing// 829 | if ((settings.balanceDuty + 5) > ((balancetimer - millis()) * 0.001)) { 830 | bms.setBalIgnore(true); 831 | /* 832 | Serial.println(); 833 | Serial.println("Ignore Voltages Balancing Active"); 834 | */ 835 | } else { 836 | bms.setBalIgnore(false); 837 | } 838 | 839 | ///Set Ids to unnasgined// 840 | 841 | if (Unassigned > 0) { 842 | assignID(); 843 | } 844 | 845 | //// 846 | 847 | resetwdog(); 848 | } 849 | if (millis() - cleartime > 5000) { 850 | //bms.clearmodules(); // Not functional 851 | if (bms.checkcomms()) { 852 | //no missing modules 853 | /* 854 | SERIALCONSOLE.println(" "); 855 | SERIALCONSOLE.print(" ALL OK NO MODULE MISSING :) "); 856 | SERIALCONSOLE.println(" "); 857 | */ 858 | if (bmsstatus == Error) { 859 | bmsstatus = Boot; 860 | } 861 | } else { 862 | //missing module 863 | if (debug != 0) { 864 | SERIALCONSOLE.println(" "); 865 | SERIALCONSOLE.print(" !!! MODULE MISSING !!!"); 866 | SERIALCONSOLE.println(" "); 867 | } 868 | bmsstatus = Error; 869 | ErrorReason = 4; 870 | } 871 | cleartime = millis(); 872 | } 873 | if (millis() - looptime1 > settings.chargerspd) { 874 | looptime1 = millis(); 875 | if (settings.ESSmode == 1) { 876 | chargercomms(); 877 | } else { 878 | if (bmsstatus == Charge) { 879 | chargercomms(); 880 | } 881 | } 882 | } 883 | } 884 | 885 | void alarmupdate() { 886 | alarm[0] = 0x00; 887 | if (settings.OverVSetpoint < bms.getHighCellVolt()) { 888 | alarm[0] = 0x04; 889 | } 890 | if (bms.getLowCellVolt() < settings.UnderVSetpoint) { 891 | alarm[0] |= 0x10; 892 | } 893 | if (bms.getHighTemperature() > settings.OverTSetpoint) { 894 | alarm[0] |= 0x40; 895 | } 896 | alarm[1] = 0; 897 | if (bms.getLowTemperature() < settings.UnderTSetpoint) { 898 | alarm[1] = 0x01; 899 | } 900 | alarm[3] = 0; 901 | if ((bms.getHighCellVolt() - bms.getLowCellVolt()) > settings.CellGap) { 902 | alarm[3] = 0x01; 903 | } 904 | 905 | ///warnings/// 906 | warning[0] = 0; 907 | 908 | if (bms.getHighCellVolt() > (settings.OverVSetpoint - settings.WarnOff)) { 909 | warning[0] = 0x04; 910 | } 911 | if (bms.getLowCellVolt() < (settings.UnderVSetpoint + settings.WarnOff)) { 912 | warning[0] |= 0x10; 913 | } 914 | 915 | if (bms.getHighTemperature() > (settings.OverTSetpoint - settings.WarnToff)) { 916 | warning[0] |= 0x40; 917 | } 918 | warning[1] = 0; 919 | if (bms.getLowTemperature() < (settings.UnderTSetpoint + settings.WarnToff)) { 920 | warning[1] = 0x01; 921 | } 922 | } 923 | 924 | void gaugeupdate() { 925 | if (gaugedebug != 0) { 926 | SOCtest = SOCtest + 5; 927 | if (SOCtest > 1000) { 928 | SOCtest = 0; 929 | } 930 | analogWrite(OUT8, map(SOCtest * 0.1, 0, 100, settings.gaugelow, settings.gaugehigh)); 931 | 932 | SERIALCONSOLE.println(" "); 933 | SERIALCONSOLE.print("SOC : "); 934 | SERIALCONSOLE.print(SOCtest * 0.1); 935 | SERIALCONSOLE.print(" fuel pwm : "); 936 | SERIALCONSOLE.print(map(SOCtest * 0.1, 0, 100, settings.gaugelow, settings.gaugehigh)); 937 | SERIALCONSOLE.println(" "); 938 | } else { 939 | analogWrite(OUT8, map(SOC, 0, 100, settings.gaugelow, settings.gaugehigh)); 940 | } 941 | } 942 | 943 | void printbmsstat() { 944 | SERIALCONSOLE.println(); 945 | SERIALCONSOLE.println(); 946 | SERIALCONSOLE.println(); 947 | SERIALCONSOLE.print("BMS Status : "); 948 | if (settings.ESSmode == 1) { 949 | SERIALCONSOLE.print("ESS Mode "); 950 | 951 | if (bms.getLowCellVolt() < settings.UnderVSetpoint) { 952 | SERIALCONSOLE.print(": UnderVoltage "); 953 | } 954 | if (bms.getHighCellVolt() > settings.OverVSetpoint) { 955 | SERIALCONSOLE.print(": OverVoltage "); 956 | } 957 | if ((bms.getHighCellVolt() - bms.getLowCellVolt()) > settings.CellGap) { 958 | SERIALCONSOLE.print(": Cell Imbalance "); 959 | } 960 | if (bms.getAvgTemperature() > settings.OverTSetpoint) { 961 | SERIALCONSOLE.print(": Over Temp "); 962 | } 963 | if (bms.getAvgTemperature() < settings.UnderTSetpoint) { 964 | SERIALCONSOLE.print(": Under Temp "); 965 | } 966 | if (storagemode == 1) { 967 | if (bms.getLowCellVolt() > settings.StoreVsetpoint) { 968 | SERIALCONSOLE.print(": OverVoltage Storage "); 969 | SERIALCONSOLE.print(": UNhappy:"); 970 | } else { 971 | SERIALCONSOLE.print(": Happy "); 972 | } 973 | } else { 974 | if (bms.getLowCellVolt() > settings.UnderVSetpoint && bms.getHighCellVolt() < settings.OverVSetpoint) { 975 | 976 | if (bmsstatus == Error) { 977 | SERIALCONSOLE.print(": UNhappy:"); 978 | } else { 979 | SERIALCONSOLE.print(": Happy "); 980 | } 981 | } 982 | } 983 | } else { 984 | SERIALCONSOLE.print(bmsstatus); 985 | switch (bmsstatus) { 986 | case (Boot): 987 | SERIALCONSOLE.print(" Boot "); 988 | break; 989 | 990 | case (Ready): 991 | SERIALCONSOLE.print(" Ready "); 992 | break; 993 | 994 | case (Precharge): 995 | SERIALCONSOLE.print(" Precharge "); 996 | break; 997 | 998 | case (Drive): 999 | SERIALCONSOLE.print(" Drive "); 1000 | break; 1001 | 1002 | case (Charge): 1003 | SERIALCONSOLE.print(" Charge "); 1004 | break; 1005 | 1006 | case (Error): 1007 | SERIALCONSOLE.print(" Error "); 1008 | break; 1009 | } 1010 | } 1011 | SERIALCONSOLE.print(" "); 1012 | if (digitalRead(IN3) == HIGH) { 1013 | SERIALCONSOLE.print("| AC Present |"); 1014 | } 1015 | if (digitalRead(IN1) == HIGH) { 1016 | SERIALCONSOLE.print("| Key ON |"); 1017 | } 1018 | if (balancecells == 1) { 1019 | SERIALCONSOLE.print("|Balancing Active"); 1020 | SERIALCONSOLE.print("|"); 1021 | //SERIALCONSOLE.print(balancepauze); 1022 | //SERIALCONSOLE.print("| Counter: "); 1023 | SERIALCONSOLE.print((balancetimer - millis()) * 0.001, 0); 1024 | SERIALCONSOLE.print("|"); 1025 | } 1026 | SERIALCONSOLE.print(" "); 1027 | SERIALCONSOLE.print(cellspresent); 1028 | SERIALCONSOLE.println(); 1029 | SERIALCONSOLE.print("Out:"); 1030 | SERIALCONSOLE.print(digitalRead(OUT1)); 1031 | SERIALCONSOLE.print(digitalRead(OUT2)); 1032 | SERIALCONSOLE.print(digitalRead(OUT3)); 1033 | SERIALCONSOLE.print(digitalRead(OUT4)); 1034 | SERIALCONSOLE.print(" Cont:"); 1035 | if ((contstat & 1) == 1) { 1036 | SERIALCONSOLE.print("1"); 1037 | } else { 1038 | SERIALCONSOLE.print("0"); 1039 | } 1040 | if ((contstat & 2) == 2) { 1041 | SERIALCONSOLE.print("1"); 1042 | } else { 1043 | SERIALCONSOLE.print("0"); 1044 | } 1045 | if ((contstat & 4) == 4) { 1046 | SERIALCONSOLE.print("1"); 1047 | } else { 1048 | SERIALCONSOLE.print("0"); 1049 | } 1050 | if ((contstat & 8) == 8) { 1051 | SERIALCONSOLE.print("1"); 1052 | } else { 1053 | SERIALCONSOLE.print("0"); 1054 | } 1055 | SERIALCONSOLE.print(" In:"); 1056 | SERIALCONSOLE.print(digitalRead(IN1)); 1057 | SERIALCONSOLE.print(digitalRead(IN2)); 1058 | SERIALCONSOLE.print(digitalRead(IN3)); 1059 | SERIALCONSOLE.print(digitalRead(IN4)); 1060 | /* 1061 | SERIALCONSOLE.print(" | "); 1062 | SERIALCONSOLE.print(bms.getLowTemperature()); 1063 | SERIALCONSOLE.print(" | "); 1064 | SERIALCONSOLE.print(bms.getHighTemperature()); 1065 | */ 1066 | 1067 | SERIALCONSOLE.print(" Charge Current Limit : "); 1068 | SERIALCONSOLE.print(chargecurrent * 0.1, 0); 1069 | SERIALCONSOLE.print(" A DisCharge Current Limit : "); 1070 | SERIALCONSOLE.print(discurrent * 0.1, 0); 1071 | SERIALCONSOLE.print(" A"); 1072 | 1073 | if (bmsstatus == Charge || accurlim > 0) { 1074 | Serial.print(" CP AC Current Limit: "); 1075 | Serial.print(accurlim); 1076 | Serial.print(" A"); 1077 | } 1078 | 1079 | if (bmsstatus == Charge && CPdebug == 1) { 1080 | Serial.print("A CP Dur: "); 1081 | Serial.print(duration); 1082 | Serial.print(" Charge Power : "); 1083 | Serial.print(chargerpower); 1084 | if (chargecurrentlimit == false) { 1085 | SERIALCONSOLE.print(" No Charge Current Limit"); 1086 | } else { 1087 | SERIALCONSOLE.print(" Charge Current Limit Active"); 1088 | } 1089 | } 1090 | } 1091 | 1092 | 1093 | void getcurrent() { 1094 | if (settings.cursens == Analoguedual || settings.cursens == Analoguesing) { 1095 | if (settings.cursens == Analoguedual) { 1096 | if (currentact < settings.changecur && currentact > (settings.changecur * -1)) { 1097 | sensor = 1; 1098 | adc->adc0->startContinuous(ACUR1); 1099 | } else { 1100 | sensor = 2; 1101 | adc->adc0->startContinuous(ACUR2); 1102 | } 1103 | } else { 1104 | sensor = 1; 1105 | adc->adc0->startContinuous(ACUR1); 1106 | } 1107 | if (sensor == 1) { 1108 | if (debugCur != 0) { 1109 | SERIALCONSOLE.println(); 1110 | if (settings.cursens == Analoguedual) { 1111 | SERIALCONSOLE.print("Low Range: "); 1112 | } else { 1113 | SERIALCONSOLE.print("Single In: "); 1114 | } 1115 | SERIALCONSOLE.print("Value ADC0: "); 1116 | } 1117 | value = (uint16_t)adc->adc0->analogReadContinuous(); // the unsigned is necessary for 16 bits, otherwise values larger than 3.3/2 V are negative! 1118 | if (debugCur != 0) { 1119 | SERIALCONSOLE.print(value * 3300 / adc->adc0->getMaxValue()); //- settings.offset1) 1120 | SERIALCONSOLE.print(" "); 1121 | SERIALCONSOLE.print(settings.offset1); 1122 | } 1123 | RawCur = int16_t((value * 3300 / adc->adc0->getMaxValue()) - settings.offset1) / (settings.convlow * 0.0000066); 1124 | 1125 | if (abs((int16_t(value * 3300 / adc->adc0->getMaxValue()) - settings.offset1)) < settings.CurDead) { 1126 | RawCur = 0; 1127 | } 1128 | if (debugCur != 0) { 1129 | SERIALCONSOLE.print(" "); 1130 | SERIALCONSOLE.print(int16_t(value * 3300 / adc->adc0->getMaxValue()) - settings.offset1); 1131 | SERIALCONSOLE.print(" "); 1132 | SERIALCONSOLE.print(RawCur); 1133 | SERIALCONSOLE.print(" mA"); 1134 | SERIALCONSOLE.print(" "); 1135 | } 1136 | } else { 1137 | if (debugCur != 0) { 1138 | SERIALCONSOLE.println(); 1139 | SERIALCONSOLE.print("High Range: "); 1140 | SERIALCONSOLE.print("Value ADC0: "); 1141 | } 1142 | value = (uint16_t)adc->adc0->analogReadContinuous(); // the unsigned is necessary for 16 bits, otherwise values larger than 3.3/2 V are negative! 1143 | if (debugCur != 0) { 1144 | SERIALCONSOLE.print(value * 3300 / adc->adc0->getMaxValue()); //- settings.offset2) 1145 | SERIALCONSOLE.print(" "); 1146 | SERIALCONSOLE.print(settings.offset2); 1147 | } 1148 | RawCur = int16_t((value * 3300 / adc->adc0->getMaxValue()) - settings.offset2) / (settings.convhigh * 0.0000066); 1149 | if (value < 100 || value > (adc->adc0->getMaxValue() - 100)) { 1150 | RawCur = 0; 1151 | } 1152 | if (debugCur != 0) { 1153 | SERIALCONSOLE.print(" "); 1154 | SERIALCONSOLE.print((float(value * 3300 / adc->adc0->getMaxValue()) - settings.offset2)); 1155 | SERIALCONSOLE.print(" "); 1156 | SERIALCONSOLE.print(RawCur); 1157 | SERIALCONSOLE.print("mA"); 1158 | SERIALCONSOLE.print(" "); 1159 | } 1160 | } 1161 | } 1162 | 1163 | if (settings.invertcur == 1) { 1164 | RawCur = RawCur * -1; 1165 | } 1166 | 1167 | lowpassFilter.input(RawCur); 1168 | if (debugCur != 0) { 1169 | SERIALCONSOLE.print(lowpassFilter.output()); 1170 | SERIALCONSOLE.print(" | "); 1171 | SERIALCONSOLE.print(settings.changecur); 1172 | SERIALCONSOLE.print(" | "); 1173 | } 1174 | 1175 | currentact = lowpassFilter.output(); 1176 | 1177 | if (debugCur != 0) { 1178 | SERIALCONSOLE.print(currentact); 1179 | SERIALCONSOLE.print("mA "); 1180 | } 1181 | 1182 | if (settings.cursens == Analoguedual) { 1183 | if (sensor == 1) { 1184 | if (currentact > 500 || currentact < -500) { 1185 | ampsecond = ampsecond + ((currentact * (millis() - lasttime) / 1000) / 1000); 1186 | lasttime = millis(); 1187 | } else { 1188 | lasttime = millis(); 1189 | } 1190 | } 1191 | if (sensor == 2) { 1192 | if (currentact > settings.changecur || currentact < (settings.changecur * -1)) { 1193 | ampsecond = ampsecond + ((currentact * (millis() - lasttime) / 1000) / 1000); 1194 | lasttime = millis(); 1195 | } else { 1196 | lasttime = millis(); 1197 | } 1198 | } 1199 | } else { 1200 | if (currentact > 500 || currentact < -500) { 1201 | ampsecond = ampsecond + ((currentact * (millis() - lasttime) / 1000) / 1000); 1202 | lasttime = millis(); 1203 | } else { 1204 | lasttime = millis(); 1205 | } 1206 | } 1207 | currentact = settings.ncur * currentact; 1208 | RawCur = 0; 1209 | /* 1210 | AverageCurrentTotal = AverageCurrentTotal - RunningAverageBuffer[NextRunningAverage]; 1211 | 1212 | RunningAverageBuffer[NextRunningAverage] = currentact; 1213 | 1214 | if (debugCur != 0) 1215 | { 1216 | SERIALCONSOLE.print(" | "); 1217 | SERIALCONSOLE.print(AverageCurrentTotal); 1218 | SERIALCONSOLE.print(" | "); 1219 | SERIALCONSOLE.print(RunningAverageBuffer[NextRunningAverage]); 1220 | SERIALCONSOLE.print(" | "); 1221 | } 1222 | AverageCurrentTotal = AverageCurrentTotal + RunningAverageBuffer[NextRunningAverage]; 1223 | if (debugCur != 0) 1224 | { 1225 | SERIALCONSOLE.print(" | "); 1226 | SERIALCONSOLE.print(AverageCurrentTotal); 1227 | SERIALCONSOLE.print(" | "); 1228 | } 1229 | 1230 | NextRunningAverage = NextRunningAverage + 1; 1231 | 1232 | if (NextRunningAverage > RunningAverageCount) 1233 | { 1234 | NextRunningAverage = 0; 1235 | } 1236 | 1237 | AverageCurrent = AverageCurrentTotal / (RunningAverageCount + 1); 1238 | 1239 | if (debugCur != 0) 1240 | { 1241 | SERIALCONSOLE.print(AverageCurrent); 1242 | SERIALCONSOLE.print(" | "); 1243 | SERIALCONSOLE.print(AverageCurrentTotal); 1244 | SERIALCONSOLE.print(" | "); 1245 | SERIALCONSOLE.print(NextRunningAverage); 1246 | } 1247 | */ 1248 | } 1249 | 1250 | 1251 | void updateSOC() { 1252 | if (SOCreset == 1) { 1253 | SOC = map(uint16_t(bms.getLowCellVolt() * 1000), settings.socvolt[0], settings.socvolt[2], settings.socvolt[1], settings.socvolt[3]); 1254 | ampsecond = (SOC * settings.CAP * settings.Pstrings * 10) / 0.27777777777778; 1255 | SOCreset = 0; 1256 | } 1257 | 1258 | if (SOCset == 0 && SOCmem == 0) { 1259 | if (millis() > 9000) { 1260 | bms.setSensors(settings.IgnoreTemp, settings.IgnoreVolt, settings.TempOff); 1261 | } 1262 | if (millis() > 10000) { 1263 | SOC = map(uint16_t(bms.getLowCellVolt() * 1000), settings.socvolt[0], settings.socvolt[2], settings.socvolt[1], settings.socvolt[3]); 1264 | 1265 | ampsecond = (SOC * settings.CAP * settings.Pstrings * 10) / 0.27777777777778; 1266 | SOCset = 1; 1267 | if (debug != 0) { 1268 | SERIALCONSOLE.println(" "); 1269 | SERIALCONSOLE.println("//////////////////////////////////////// SOC SET ////////////////////////////////////////"); 1270 | } 1271 | if (settings.ESSmode == 1) { 1272 | bmsstatus = Ready; 1273 | } 1274 | } 1275 | } 1276 | 1277 | if (SOCset == 0 && SOCmem == 1) { 1278 | ampsecond = (SOC * settings.CAP * settings.Pstrings * 10) / 0.27777777777778; 1279 | if (millis() > 9000) { 1280 | bms.setSensors(settings.IgnoreTemp, settings.IgnoreVolt, settings.TempOff); 1281 | } 1282 | if (millis() > 10000) { 1283 | SOCset = 1; 1284 | if (debug != 0) { 1285 | SERIALCONSOLE.println(" "); 1286 | SERIALCONSOLE.println("//////////////////////////////////////// SOC SET ////////////////////////////////////////"); 1287 | } 1288 | if (settings.ESSmode == 1) { 1289 | bmsstatus = Ready; 1290 | } 1291 | } 1292 | } 1293 | 1294 | SOC = ((ampsecond * 0.27777777777778) / (settings.CAP * settings.Pstrings * 1000)) * 100; 1295 | 1296 | if (settings.voltsoc == 1 || settings.cursens == 0) { 1297 | SOC = map(uint16_t(bms.getLowCellVolt() * 1000), settings.socvolt[0], settings.socvolt[2], settings.socvolt[1], settings.socvolt[3]); 1298 | 1299 | ampsecond = (SOC * settings.CAP * settings.Pstrings * 10) / 0.27777777777778; 1300 | } 1301 | 1302 | if (SOC >= 100) { 1303 | ampsecond = (settings.CAP * settings.Pstrings * 1000) / 0.27777777777778; //reset to full, dependant on given capacity. Need to improve with auto correction for capcity. 1304 | SOC = 100; 1305 | } 1306 | 1307 | 1308 | if (SOC < 0) { 1309 | SOC = 0; //reset SOC this way the can messages remain in range for other devices. Ampseconds will keep counting. 1310 | } 1311 | 1312 | if (debug != 0) { 1313 | if (settings.cursens == Analoguedual) { 1314 | if (sensor == 1) { 1315 | SERIALCONSOLE.print("Low Range "); 1316 | } else { 1317 | SERIALCONSOLE.print("High Range"); 1318 | } 1319 | } 1320 | if (settings.cursens == Analoguesing) { 1321 | SERIALCONSOLE.print("Analogue Single "); 1322 | } 1323 | if (settings.cursens == Canbus) { 1324 | SERIALCONSOLE.print("CANbus "); 1325 | } 1326 | SERIALCONSOLE.print(" "); 1327 | SERIALCONSOLE.print(currentact); 1328 | SERIALCONSOLE.print("mA"); 1329 | SERIALCONSOLE.print(" "); 1330 | SERIALCONSOLE.print(SOC); 1331 | SERIALCONSOLE.print("% SOC "); 1332 | SERIALCONSOLE.print(ampsecond * 0.27777777777778, 2); 1333 | SERIALCONSOLE.print("mAh"); 1334 | } 1335 | } 1336 | 1337 | void SOCcharged(int y) { 1338 | if (y == 1) { 1339 | SOC = 95; 1340 | ampsecond = (settings.CAP * settings.Pstrings * 1000) / 0.27777777777778; //reset to full, dependant on given capacity. Need to improve with auto correction for capcity. 1341 | } 1342 | if (y == 2) { 1343 | SOC = 100; 1344 | ampsecond = (settings.CAP * settings.Pstrings * 1000) / 0.27777777777778; //reset to full, dependant on given capacity. Need to improve with auto correction for capcity. 1345 | } 1346 | } 1347 | 1348 | void Prechargecon() { 1349 | if (digitalRead(IN1) == HIGH || digitalRead(IN3) == HIGH) //detect Key ON or AC present 1350 | { 1351 | digitalWrite(OUT4, HIGH); //Negative Contactor Close 1352 | contctrl = 2; 1353 | if (Pretimer + settings.Pretime > millis() || currentact > settings.Precurrent) { 1354 | digitalWrite(OUT2, HIGH); //precharge 1355 | } else //close main contactor 1356 | { 1357 | digitalWrite(OUT1, HIGH); //Positive Contactor Close 1358 | contctrl = 3; 1359 | if (settings.ChargerDirect == 1) { 1360 | bmsstatus = Drive; 1361 | } else { 1362 | if (digitalRead(IN3) == HIGH) { 1363 | bmsstatus = Charge; 1364 | } 1365 | if (digitalRead(IN1) == HIGH) { 1366 | bmsstatus = Drive; 1367 | } 1368 | } 1369 | digitalWrite(OUT2, LOW); 1370 | } 1371 | } else { 1372 | digitalWrite(OUT1, LOW); 1373 | digitalWrite(OUT2, LOW); 1374 | digitalWrite(OUT4, LOW); 1375 | bmsstatus = Ready; 1376 | contctrl = 0; 1377 | } 1378 | } 1379 | 1380 | void contcon() { 1381 | if (contctrl != contstat) //check for contactor request change 1382 | { 1383 | if ((contctrl & 1) == 0) { 1384 | analogWrite(OUT5, 0); 1385 | contstat = contstat & 254; 1386 | } 1387 | if ((contctrl & 2) == 0) { 1388 | analogWrite(OUT6, 0); 1389 | contstat = contstat & 253; 1390 | } 1391 | if ((contctrl & 4) == 0) { 1392 | analogWrite(OUT7, 0); 1393 | contstat = contstat & 251; 1394 | } 1395 | 1396 | 1397 | if ((contctrl & 1) == 1) { 1398 | if ((contstat & 1) != 1) { 1399 | if (conttimer1 == 0) { 1400 | analogWrite(OUT5, 255); 1401 | conttimer1 = millis() + pulltime; 1402 | } 1403 | if (conttimer1 < millis()) { 1404 | analogWrite(OUT5, settings.conthold); 1405 | contstat = contstat | 1; 1406 | conttimer1 = 0; 1407 | } 1408 | } 1409 | } 1410 | 1411 | if ((contctrl & 2) == 2) { 1412 | if ((contstat & 2) != 2) { 1413 | if (conttimer2 == 0) { 1414 | Serial.println(); 1415 | Serial.println("pull in OUT6"); 1416 | analogWrite(OUT6, 255); 1417 | conttimer2 = millis() + pulltime; 1418 | } 1419 | if (conttimer2 < millis()) { 1420 | analogWrite(OUT6, settings.conthold); 1421 | contstat = contstat | 2; 1422 | conttimer2 = 0; 1423 | } 1424 | } 1425 | } 1426 | if ((contctrl & 4) == 4) { 1427 | if ((contstat & 4) != 4) { 1428 | if (conttimer3 == 0) { 1429 | Serial.println(); 1430 | Serial.println("pull in OUT7"); 1431 | analogWrite(OUT7, 255); 1432 | conttimer3 = millis() + pulltime; 1433 | } 1434 | if (conttimer3 < millis()) { 1435 | analogWrite(OUT7, settings.conthold); 1436 | contstat = contstat | 4; 1437 | conttimer3 = 0; 1438 | } 1439 | } 1440 | } 1441 | /* 1442 | SERIALCONSOLE.print(conttimer); 1443 | SERIALCONSOLE.print(" "); 1444 | SERIALCONSOLE.print(contctrl); 1445 | SERIALCONSOLE.print(" "); 1446 | SERIALCONSOLE.print(contstat); 1447 | SERIALCONSOLE.println(" "); 1448 | */ 1449 | } 1450 | if (contctrl == 0) { 1451 | analogWrite(OUT5, 0); 1452 | analogWrite(OUT6, 0); 1453 | } 1454 | } 1455 | 1456 | void calcur() { 1457 | adc->adc0->startContinuous(ACUR1); 1458 | sensor = 1; 1459 | x = 0; 1460 | SERIALCONSOLE.print(" Calibrating Current Offset ::::: "); 1461 | while (x < 20) { 1462 | settings.offset1 = settings.offset1 + ((uint16_t)adc->adc0->analogReadContinuous() * 3300 / adc->adc0->getMaxValue()); 1463 | SERIALCONSOLE.print("."); 1464 | delay(100); 1465 | x++; 1466 | } 1467 | settings.offset1 = settings.offset1 / 21; 1468 | SERIALCONSOLE.print(settings.offset1); 1469 | SERIALCONSOLE.print(" current offset 1 calibrated "); 1470 | SERIALCONSOLE.println(" "); 1471 | x = 0; 1472 | adc->startContinuous(ACUR2, ADC_0); 1473 | sensor = 2; 1474 | SERIALCONSOLE.print(" Calibrating Current Offset ::::: "); 1475 | while (x < 20) { 1476 | settings.offset2 = settings.offset2 + ((uint16_t)adc->adc0->analogReadContinuous() * 3300 / adc->adc0->getMaxValue()); 1477 | SERIALCONSOLE.print("."); 1478 | delay(100); 1479 | x++; 1480 | } 1481 | settings.offset2 = settings.offset2 / 21; 1482 | SERIALCONSOLE.print(settings.offset2); 1483 | SERIALCONSOLE.print(" current offset 2 calibrated "); 1484 | SERIALCONSOLE.println(" "); 1485 | } 1486 | 1487 | void VEcan() //communication with Victron system over CAN 1488 | { 1489 | if (settings.chargertype == 6) { 1490 | msg.id = 0x618; 1491 | msg.len = 8; 1492 | msg.buf[0] = 0x00; 1493 | msg.buf[1] = 'B'; 1494 | msg.buf[2] = 'Y'; 1495 | msg.buf[3] = 'D'; 1496 | msg.buf[4] = 0x00; 1497 | msg.buf[5] = 0x00; 1498 | msg.buf[6] = 0x00; 1499 | msg.buf[7] = 0x00; 1500 | Can0.write(msg); 1501 | 1502 | delay(2); 1503 | msg.id = 0x5D8; 1504 | msg.len = 8; 1505 | msg.buf[0] = 0x00; 1506 | msg.buf[1] = 'B'; 1507 | msg.buf[2] = 'Y'; 1508 | msg.buf[3] = 'D'; 1509 | msg.buf[4] = 0x00; 1510 | msg.buf[5] = 0x00; 1511 | msg.buf[6] = 0x00; 1512 | msg.buf[7] = 0x00; 1513 | Can0.write(msg); 1514 | 1515 | delay(2); 1516 | 1517 | msg.id = 0x558; 1518 | msg.len = 8; 1519 | msg.buf[0] = 0x03; 1520 | msg.buf[1] = 0x12; 1521 | msg.buf[2] = 0x00; 1522 | msg.buf[3] = 0x04; 1523 | msg.buf[4] = highByte(settings.CAP * settings.Pstrings * 37 * settings.Scells); 1524 | msg.buf[5] = lowByte(settings.CAP * settings.Pstrings * 37 * settings.Scells); 1525 | msg.buf[6] = 0x05; 1526 | msg.buf[7] = 0x07; 1527 | Can0.write(msg); 1528 | 1529 | delay(2); 1530 | 1531 | msg.id = 0x598; 1532 | msg.len = 8; 1533 | msg.buf[0] = 0x00; 1534 | msg.buf[1] = 0x00; 1535 | msg.buf[2] = 0x12; 1536 | msg.buf[3] = 0x34; 1537 | msg.buf[4] = 0x00; 1538 | msg.buf[5] = 0x00; 1539 | msg.buf[6] = 0x04; 1540 | msg.buf[7] = 0x4F; 1541 | Can0.write(msg); 1542 | 1543 | delay(2); 1544 | 1545 | msg.id = 0x358; 1546 | msg.len = 8; 1547 | if (storagemode == 0) { 1548 | msg.buf[0] = highByte(uint16_t((settings.ChargeVsetpoint * settings.Scells) * 10)); 1549 | msg.buf[1] = lowByte(uint16_t((settings.ChargeVsetpoint * settings.Scells) * 10)); 1550 | } else { 1551 | msg.buf[0] = highByte(uint16_t((settings.StoreVsetpoint * settings.Scells) * 10)); 1552 | msg.buf[1] = lowByte(uint16_t((settings.StoreVsetpoint * settings.Scells) * 10)); 1553 | } 1554 | msg.buf[2] = highByte(uint16_t((settings.DischVsetpoint * settings.Scells) * 10)); 1555 | msg.buf[3] = lowByte(uint16_t((settings.DischVsetpoint * settings.Scells) * 10)); 1556 | msg.buf[4] = highByte(discurrent); 1557 | msg.buf[5] = lowByte(discurrent); 1558 | msg.buf[6] = highByte(chargecurrent); 1559 | msg.buf[7] = lowByte(chargecurrent); 1560 | Can0.write(msg); 1561 | 1562 | delay(2); 1563 | 1564 | msg.id = 0x3D8; 1565 | msg.len = 8; 1566 | msg.buf[0] = highByte(SOC * 100); 1567 | msg.buf[1] = lowByte(SOC * 100); 1568 | msg.buf[2] = highByte(SOH); 1569 | msg.buf[3] = lowByte(SOH); 1570 | msg.buf[4] = highByte(uint16_t(ampsecond * 0.002777778)); 1571 | msg.buf[5] = lowByte(uint16_t(ampsecond * 0.002777778)); 1572 | msg.buf[6] = 0xF9; 1573 | msg.buf[7] = 0; 1574 | Can0.write(msg); 1575 | 1576 | delay(2); 1577 | 1578 | msg.id = 0x458; 1579 | msg.len = 8; 1580 | msg.buf[0] = 0x00; 1581 | msg.buf[1] = 0x00; 1582 | msg.buf[2] = 0x12; 1583 | msg.buf[3] = 0x34; 1584 | msg.buf[4] = 0x00; 1585 | msg.buf[5] = 0x00; 1586 | msg.buf[6] = 0x56; 1587 | msg.buf[7] = 0x78; 1588 | Can0.write(msg); 1589 | 1590 | delay(2); 1591 | 1592 | msg.id = 0x518; 1593 | msg.len = 8; 1594 | msg.buf[0] = highByte(uint16_t(bms.getHighTemperature() * 10)); 1595 | msg.buf[1] = lowByte(uint16_t(bms.getHighTemperature() * 10)); 1596 | msg.buf[2] = highByte(uint16_t(bms.getLowTemperature() * 10)); 1597 | msg.buf[3] = lowByte(uint16_t(bms.getLowTemperature() * 10)); 1598 | msg.buf[4] = 0xFF; 1599 | msg.buf[5] = 0xFF; 1600 | msg.buf[6] = 0xFF; 1601 | msg.buf[7] = 0xFF; 1602 | 1603 | Can0.write(msg); 1604 | 1605 | delay(2); 1606 | 1607 | msg.id = 0x4D8; 1608 | msg.len = 8; 1609 | msg.buf[0] = highByte(uint16_t(bms.getPackVoltage() * 10)); 1610 | msg.buf[1] = lowByte(uint16_t(bms.getPackVoltage() * 10)); 1611 | msg.buf[2] = highByte(long(currentact / 100)); 1612 | msg.buf[3] = lowByte(long(currentact / 100)); 1613 | msg.buf[4] = highByte(int16_t(bms.getAvgTemperature() * 10)); 1614 | msg.buf[5] = lowByte(int16_t(bms.getAvgTemperature() * 10)); 1615 | msg.buf[6] = 0x03; 1616 | msg.buf[7] = 0x08; 1617 | Can0.write(msg); 1618 | 1619 | delay(2); 1620 | msg.id = 0x158; 1621 | msg.len = 8; 1622 | msg.buf[0] = alarm[0]; //High temp Low Voltage | High Voltage 1623 | msg.buf[1] = alarm[1]; // High Discharge Current | Low Temperature 1624 | msg.buf[2] = alarm[2]; //Internal Failure | High Charge current 1625 | msg.buf[3] = alarm[3]; // Cell Imbalance 1626 | msg.buf[4] = warning[0]; //High temp Low Voltage | High Voltage 1627 | msg.buf[5] = warning[1]; // High Discharge Current | Low Temperature 1628 | msg.buf[6] = warning[2]; //Internal Failure | High Charge current 1629 | msg.buf[7] = warning[3]; // Cell Imbalance 1630 | Can0.write(msg); 1631 | 1632 | } else { 1633 | msg.id = 0x351; 1634 | msg.len = 8; 1635 | if (storagemode == 0) { 1636 | msg.buf[0] = lowByte(uint16_t((settings.ChargeVsetpoint * settings.Scells) * 10)); 1637 | msg.buf[1] = highByte(uint16_t((settings.ChargeVsetpoint * settings.Scells) * 10)); 1638 | } else { 1639 | msg.buf[0] = lowByte(uint16_t((settings.StoreVsetpoint * settings.Scells) * 10)); 1640 | msg.buf[1] = highByte(uint16_t((settings.StoreVsetpoint * settings.Scells) * 10)); 1641 | } 1642 | msg.buf[2] = lowByte(chargecurrent); 1643 | msg.buf[3] = highByte(chargecurrent); 1644 | msg.buf[4] = lowByte(discurrent); 1645 | msg.buf[5] = highByte(discurrent); 1646 | msg.buf[6] = lowByte(uint16_t((settings.DischVsetpoint * settings.Scells) * 10)); 1647 | msg.buf[7] = highByte(uint16_t((settings.DischVsetpoint * settings.Scells) * 10)); 1648 | Can0.write(msg); 1649 | 1650 | msg.id = 0x355; 1651 | msg.len = 8; 1652 | msg.buf[0] = lowByte(SOC); 1653 | msg.buf[1] = highByte(SOC); 1654 | msg.buf[2] = lowByte(SOH); 1655 | msg.buf[3] = highByte(SOH); 1656 | msg.buf[4] = lowByte(SOC * 10); 1657 | msg.buf[5] = highByte(SOC * 10); 1658 | msg.buf[6] = 0; 1659 | msg.buf[7] = 0; 1660 | Can0.write(msg); 1661 | 1662 | msg.id = 0x356; 1663 | msg.len = 8; 1664 | msg.buf[0] = lowByte(uint16_t(bms.getPackVoltage() * 100)); 1665 | msg.buf[1] = highByte(uint16_t(bms.getPackVoltage() * 100)); 1666 | msg.buf[2] = lowByte(long(currentact / 100)); 1667 | msg.buf[3] = highByte(long(currentact / 100)); 1668 | msg.buf[4] = lowByte(int16_t(bms.getAvgTemperature() * 10)); 1669 | msg.buf[5] = highByte(int16_t(bms.getAvgTemperature() * 10)); 1670 | msg.buf[6] = 0; 1671 | msg.buf[7] = 0; 1672 | Can0.write(msg); 1673 | 1674 | delay(2); 1675 | msg.id = 0x35A; 1676 | msg.len = 8; 1677 | msg.buf[0] = alarm[0]; //High temp Low Voltage | High Voltage 1678 | msg.buf[1] = alarm[1]; // High Discharge Current | Low Temperature 1679 | msg.buf[2] = alarm[2]; //Internal Failure | High Charge current 1680 | msg.buf[3] = alarm[3]; // Cell Imbalance 1681 | msg.buf[4] = warning[0]; //High temp Low Voltage | High Voltage 1682 | msg.buf[5] = warning[1]; // High Discharge Current | Low Temperature 1683 | msg.buf[6] = warning[2]; //Internal Failure | High Charge current 1684 | msg.buf[7] = warning[3]; // Cell Imbalance 1685 | Can0.write(msg); 1686 | 1687 | msg.id = 0x35E; 1688 | msg.len = 8; 1689 | msg.buf[0] = bmsname[0]; 1690 | msg.buf[1] = bmsname[1]; 1691 | msg.buf[2] = bmsname[2]; 1692 | msg.buf[3] = bmsname[3]; 1693 | msg.buf[4] = bmsname[4]; 1694 | msg.buf[5] = bmsname[5]; 1695 | msg.buf[6] = bmsname[6]; 1696 | msg.buf[7] = bmsname[7]; 1697 | Can0.write(msg); 1698 | 1699 | delay(2); 1700 | msg.id = 0x370; 1701 | msg.len = 8; 1702 | msg.buf[0] = bmsmanu[0]; 1703 | msg.buf[1] = bmsmanu[1]; 1704 | msg.buf[2] = bmsmanu[2]; 1705 | msg.buf[3] = bmsmanu[3]; 1706 | msg.buf[4] = bmsmanu[4]; 1707 | msg.buf[5] = bmsmanu[5]; 1708 | msg.buf[6] = bmsmanu[6]; 1709 | msg.buf[7] = bmsmanu[7]; 1710 | Can0.write(msg); 1711 | 1712 | if (balancecells == 1) { 1713 | if (bms.getLowCellVolt() + settings.balanceHyst < bms.getHighCellVolt()) { 1714 | msg.id = 0x3c3; 1715 | msg.len = 8; 1716 | if (bms.getLowCellVolt() < settings.balanceVoltage) { 1717 | msg.buf[0] = highByte(uint16_t(settings.balanceVoltage * 1000)); 1718 | msg.buf[1] = lowByte(uint16_t(settings.balanceVoltage * 1000)); 1719 | } else { 1720 | msg.buf[0] = highByte(uint16_t(bms.getLowCellVolt() * 1000)); 1721 | msg.buf[1] = lowByte(uint16_t(bms.getLowCellVolt() * 1000)); 1722 | } 1723 | msg.buf[2] = 0x01; 1724 | msg.buf[3] = 0x04; 1725 | msg.buf[4] = 0x03; 1726 | msg.buf[5] = 0x00; 1727 | msg.buf[6] = 0x00; 1728 | msg.buf[7] = 0x00; 1729 | Can0.write(msg); 1730 | } 1731 | } 1732 | 1733 | delay(2); 1734 | msg.id = 0x373; 1735 | msg.len = 8; 1736 | msg.buf[0] = lowByte(uint16_t(bms.getLowCellVolt() * 1000)); 1737 | msg.buf[1] = highByte(uint16_t(bms.getLowCellVolt() * 1000)); 1738 | msg.buf[2] = lowByte(uint16_t(bms.getHighCellVolt() * 1000)); 1739 | msg.buf[3] = highByte(uint16_t(bms.getHighCellVolt() * 1000)); 1740 | msg.buf[4] = lowByte(uint16_t(bms.getLowTemperature() + 273.15)); 1741 | msg.buf[5] = highByte(uint16_t(bms.getLowTemperature() + 273.15)); 1742 | msg.buf[6] = lowByte(uint16_t(bms.getHighTemperature() + 273.15)); 1743 | msg.buf[7] = highByte(uint16_t(bms.getHighTemperature() + 273.15)); 1744 | Can0.write(msg); 1745 | 1746 | delay(2); 1747 | msg.id = 0x379; //Installed capacity 1748 | msg.len = 2; 1749 | msg.buf[0] = lowByte(uint16_t(settings.Pstrings * settings.CAP)); 1750 | msg.buf[1] = highByte(uint16_t(settings.Pstrings * settings.CAP)); 1751 | /* 1752 | delay(2); 1753 | msg.id = 0x378; //Installed capacity 1754 | msg.len = 2; 1755 | //energy in 100wh/unit 1756 | msg.buf[0] = 1757 | msg.buf[1] = 1758 | msg.buf[2] = 1759 | msg.buf[3] = 1760 | //energy out 100wh/unit 1761 | msg.buf[4] = 1762 | msg.buf[5] = 1763 | msg.buf[6] = 1764 | msg.buf[7] = 1765 | */ 1766 | delay(2); 1767 | msg.id = 0x372; 1768 | msg.len = 8; 1769 | msg.buf[0] = lowByte(bms.getNumModules()); 1770 | msg.buf[1] = highByte(bms.getNumModules()); 1771 | msg.buf[2] = 0x00; 1772 | msg.buf[3] = 0x00; 1773 | msg.buf[4] = 0x00; 1774 | msg.buf[5] = 0x00; 1775 | msg.buf[6] = 0x00; 1776 | msg.buf[7] = 0x00; 1777 | Can0.write(msg); 1778 | } 1779 | } 1780 | 1781 | 1782 | void BMVmessage() //communication with the Victron Color Control System over VEdirect 1783 | { 1784 | lasttime = millis(); 1785 | x = 0; 1786 | VE.write(13); 1787 | VE.write(10); 1788 | VE.write(myStrings[0]); 1789 | VE.write(9); 1790 | VE.print(bms.getPackVoltage() * 1000, 0); 1791 | VE.write(13); 1792 | VE.write(10); 1793 | VE.write(myStrings[2]); 1794 | VE.write(9); 1795 | VE.print(currentact); 1796 | VE.write(13); 1797 | VE.write(10); 1798 | VE.write(myStrings[4]); 1799 | VE.write(9); 1800 | VE.print(ampsecond * 0.27777777777778, 0); //consumed ah 1801 | VE.write(13); 1802 | VE.write(10); 1803 | VE.write(myStrings[6]); 1804 | VE.write(9); 1805 | VE.print(SOC * 10); //SOC 1806 | x = 8; 1807 | while (x < 20) { 1808 | VE.write(13); 1809 | VE.write(10); 1810 | VE.write(myStrings[x]); 1811 | x++; 1812 | VE.write(9); 1813 | VE.write(myStrings[x]); 1814 | x++; 1815 | } 1816 | VE.write(13); 1817 | VE.write(10); 1818 | VE.write("Checksum"); 1819 | VE.write(9); 1820 | VE.write(0x50); //0x59 1821 | delay(10); 1822 | 1823 | while (x < 44) { 1824 | VE.write(13); 1825 | VE.write(10); 1826 | VE.write(myStrings[x]); 1827 | x++; 1828 | VE.write(9); 1829 | VE.write(myStrings[x]); 1830 | x++; 1831 | } 1832 | /* 1833 | VE.write(13); 1834 | VE.write(10); 1835 | VE.write(myStrings[32]); 1836 | VE.write(9); 1837 | VE.print(bms.getLowVoltage()*1000,0); 1838 | VE.write(13); 1839 | VE.write(10); 1840 | VE.write(myStrings[34]); 1841 | VE.write(9); 1842 | VE.print(bms.getHighVoltage()*1000,0); 1843 | x=36; 1844 | 1845 | while(x < 43) 1846 | { 1847 | VE.write(13); 1848 | VE.write(10); 1849 | VE.write(myStrings[x]); 1850 | x ++; 1851 | VE.write(9); 1852 | VE.write(myStrings[x]); 1853 | x ++; 1854 | } 1855 | */ 1856 | VE.write(13); 1857 | VE.write(10); 1858 | VE.write("Checksum"); 1859 | VE.write(9); 1860 | VE.write(231); 1861 | } 1862 | 1863 | // Settings menu 1864 | // Settings menu 1865 | void menu() { 1866 | 1867 | incomingByte = Serial.read(); // read the incoming byte: 1868 | if (menuload == 4) { 1869 | switch (incomingByte) { 1870 | 1871 | case '1': 1872 | menuload = 1; 1873 | candebug = !candebug; 1874 | incomingByte = 'd'; 1875 | break; 1876 | 1877 | case '2': 1878 | menuload = 1; 1879 | debugCur = !debugCur; 1880 | incomingByte = 'd'; 1881 | break; 1882 | 1883 | case '3': 1884 | menuload = 1; 1885 | outputcheck = !outputcheck; 1886 | if (outputcheck == 0) { 1887 | contctrl = 0; 1888 | digitalWrite(OUT1, LOW); 1889 | digitalWrite(OUT2, LOW); 1890 | digitalWrite(OUT3, LOW); 1891 | digitalWrite(OUT4, LOW); 1892 | } 1893 | incomingByte = 'd'; 1894 | break; 1895 | 1896 | case '4': 1897 | menuload = 1; 1898 | inputcheck = !inputcheck; 1899 | incomingByte = 'd'; 1900 | break; 1901 | 1902 | case '5': 1903 | menuload = 1; 1904 | settings.ESSmode = !settings.ESSmode; 1905 | incomingByte = 'd'; 1906 | break; 1907 | 1908 | case '6': 1909 | menuload = 1; 1910 | cellspresent = bms.seriescells(); 1911 | incomingByte = 'd'; 1912 | break; 1913 | 1914 | case '7': 1915 | menuload = 1; 1916 | gaugedebug = !gaugedebug; 1917 | incomingByte = 'd'; 1918 | break; 1919 | 1920 | case '8': 1921 | menuload = 1; 1922 | CSVdebug = !CSVdebug; 1923 | incomingByte = 'd'; 1924 | break; 1925 | 1926 | case '9': 1927 | menuload = 1; 1928 | if (Serial.available() > 0) { 1929 | debugdigits = Serial.parseInt(); 1930 | } 1931 | if (debugdigits > 4) { 1932 | debugdigits = 2; 1933 | } 1934 | incomingByte = 'd'; 1935 | break; 1936 | 1937 | case 'b': 1938 | menuload = 1; 1939 | if (Serial.available() > 0) { 1940 | settings.balanceDuty = Serial.parseInt(); 1941 | } 1942 | 1943 | incomingByte = 'd'; 1944 | break; 1945 | 1946 | case 'r': 1947 | menuload = 1; 1948 | resetbalancedebug(); 1949 | incomingByte = 'd'; 1950 | break; 1951 | 1952 | case 'x': 1953 | menuload = 1; 1954 | resetIDdebug(); 1955 | incomingByte = 'd'; 1956 | break; 1957 | 1958 | case 'y': 1959 | menuload = 1; 1960 | if (Serial.available() > 0) { 1961 | NextID = Serial.parseInt(); 1962 | } 1963 | 1964 | incomingByte = 'd'; 1965 | break; 1966 | 1967 | case 113: //q for quite menu 1968 | 1969 | menuload = 0; 1970 | incomingByte = 115; 1971 | break; 1972 | 1973 | default: 1974 | // if nothing else matches, do the default 1975 | // default is optional 1976 | break; 1977 | } 1978 | } 1979 | 1980 | if (menuload == 2) { 1981 | switch (incomingByte) { 1982 | 1983 | 1984 | case 99: //c for calibrate zero offset 1985 | 1986 | calcur(); 1987 | break; 1988 | 1989 | case '1': 1990 | menuload = 1; 1991 | settings.invertcur = !settings.invertcur; 1992 | incomingByte = 'c'; 1993 | break; 1994 | 1995 | case '2': 1996 | menuload = 1; 1997 | settings.voltsoc = !settings.voltsoc; 1998 | incomingByte = 'c'; 1999 | break; 2000 | 2001 | case '3': 2002 | menuload = 1; 2003 | if (Serial.available() > 0) { 2004 | settings.ncur = Serial.parseInt(); 2005 | } 2006 | menuload = 1; 2007 | incomingByte = 'c'; 2008 | break; 2009 | 2010 | case '8': 2011 | menuload = 1; 2012 | if (Serial.available() > 0) { 2013 | settings.changecur = Serial.parseInt(); 2014 | } 2015 | menuload = 1; 2016 | incomingByte = 'c'; 2017 | break; 2018 | 2019 | case '4': 2020 | menuload = 1; 2021 | if (Serial.available() > 0) { 2022 | settings.convlow = Serial.parseInt(); 2023 | } 2024 | incomingByte = 'c'; 2025 | break; 2026 | 2027 | case '5': 2028 | menuload = 1; 2029 | if (Serial.available() > 0) { 2030 | settings.convhigh = Serial.parseInt(); 2031 | } 2032 | incomingByte = 'c'; 2033 | break; 2034 | 2035 | case '6': 2036 | menuload = 1; 2037 | if (Serial.available() > 0) { 2038 | settings.CurDead = Serial.parseInt(); 2039 | } 2040 | incomingByte = 'c'; 2041 | break; 2042 | 2043 | case 113: //q for quite menu 2044 | 2045 | menuload = 0; 2046 | incomingByte = 115; 2047 | break; 2048 | 2049 | case 115: //s for switch sensor 2050 | settings.cursens++; 2051 | if (settings.cursens > 3) { 2052 | settings.cursens = 0; 2053 | } 2054 | /* 2055 | if (settings.cursens == Analoguedual) 2056 | { 2057 | settings.cursens = Canbus; 2058 | SERIALCONSOLE.println(" "); 2059 | SERIALCONSOLE.print(" CANbus Current Sensor "); 2060 | SERIALCONSOLE.println(" "); 2061 | } 2062 | else 2063 | { 2064 | settings.cursens = Analoguedual; 2065 | SERIALCONSOLE.println(" "); 2066 | SERIALCONSOLE.print(" Analogue Current Sensor "); 2067 | SERIALCONSOLE.println(" "); 2068 | } 2069 | */ 2070 | menuload = 1; 2071 | incomingByte = 'c'; 2072 | break; 2073 | 2074 | case '7': //s for switch sensor 2075 | settings.curcan++; 2076 | if (settings.curcan > CurCanMax) { 2077 | settings.curcan = 1; 2078 | } 2079 | menuload = 1; 2080 | incomingByte = 'c'; 2081 | break; 2082 | 2083 | default: 2084 | // if nothing else matches, do the default 2085 | // default is optional 2086 | break; 2087 | } 2088 | } 2089 | 2090 | if (menuload == 8) { 2091 | switch (incomingByte) { 2092 | case '1': //e dispaly settings 2093 | if (Serial.available() > 0) { 2094 | settings.IgnoreTemp = Serial.parseInt(); 2095 | } 2096 | if (settings.IgnoreTemp > 2) { 2097 | settings.IgnoreTemp = 0; 2098 | } 2099 | bms.setSensors(settings.IgnoreTemp, settings.IgnoreVolt, settings.TempOff); 2100 | menuload = 1; 2101 | incomingByte = 'i'; 2102 | break; 2103 | 2104 | case '2': 2105 | if (Serial.available() > 0) { 2106 | settings.IgnoreVolt = Serial.parseInt(); 2107 | settings.IgnoreVolt = settings.IgnoreVolt * 0.001; 2108 | bms.setSensors(settings.IgnoreTemp, settings.IgnoreVolt, settings.TempOff); 2109 | // Serial.println(settings.IgnoreVolt); 2110 | menuload = 1; 2111 | incomingByte = 'i'; 2112 | } 2113 | break; 2114 | 2115 | case '4': 2116 | if (Serial.available() > 0) { 2117 | settings.TempOff = Serial.parseInt(); 2118 | bms.setSensors(settings.IgnoreTemp, settings.IgnoreVolt, settings.TempOff); 2119 | // Serial.println(settings.IgnoreVolt); 2120 | menuload = 1; 2121 | incomingByte = 'i'; 2122 | } 2123 | break; 2124 | 2125 | case 113: //q to go back to main menu 2126 | 2127 | menuload = 0; 2128 | incomingByte = 115; 2129 | break; 2130 | } 2131 | } 2132 | 2133 | 2134 | 2135 | if (menuload == 7) { 2136 | switch (incomingByte) { 2137 | case '1': 2138 | if (Serial.available() > 0) { 2139 | settings.WarnOff = Serial.parseInt(); 2140 | settings.WarnOff = settings.WarnOff * 0.001; 2141 | menuload = 1; 2142 | incomingByte = 'a'; 2143 | } 2144 | break; 2145 | 2146 | case '2': 2147 | if (Serial.available() > 0) { 2148 | settings.CellGap = Serial.parseInt(); 2149 | settings.CellGap = settings.CellGap * 0.001; 2150 | menuload = 1; 2151 | incomingByte = 'a'; 2152 | } 2153 | break; 2154 | 2155 | case '3': 2156 | if (Serial.available() > 0) { 2157 | settings.WarnToff = Serial.parseInt(); 2158 | menuload = 1; 2159 | incomingByte = 'a'; 2160 | } 2161 | break; 2162 | 2163 | case '4': 2164 | if (Serial.available() > 0) { 2165 | settings.triptime = Serial.parseInt(); 2166 | menuload = 1; 2167 | incomingByte = 'a'; 2168 | } 2169 | break; 2170 | 2171 | case 113: //q to go back to main menu 2172 | menuload = 0; 2173 | incomingByte = 115; 2174 | break; 2175 | } 2176 | } 2177 | 2178 | if (menuload == 6) //Charging settings 2179 | { 2180 | switch (incomingByte) { 2181 | 2182 | case 113: //q to go back to main menu 2183 | 2184 | menuload = 0; 2185 | incomingByte = 115; 2186 | break; 2187 | 2188 | case '1': 2189 | if (Serial.available() > 0) { 2190 | settings.ChargeVsetpoint = Serial.parseInt(); 2191 | settings.ChargeVsetpoint = settings.ChargeVsetpoint / 1000; 2192 | menuload = 1; 2193 | incomingByte = 'e'; 2194 | } 2195 | break; 2196 | 2197 | 2198 | case '2': 2199 | if (Serial.available() > 0) { 2200 | settings.ChargeHys = Serial.parseInt(); 2201 | settings.ChargeHys = settings.ChargeHys / 1000; 2202 | menuload = 1; 2203 | incomingByte = 'e'; 2204 | } 2205 | break; 2206 | 2207 | 2208 | case '4': 2209 | if (Serial.available() > 0) { 2210 | settings.chargecurrentend = Serial.parseInt() * 10; 2211 | menuload = 1; 2212 | incomingByte = 'e'; 2213 | } 2214 | break; 2215 | 2216 | 2217 | case '3': 2218 | if (Serial.available() > 0) { 2219 | settings.chargecurrentmax = Serial.parseInt() * 10; 2220 | menuload = 1; 2221 | incomingByte = 'e'; 2222 | } 2223 | break; 2224 | 2225 | case 'a': 2226 | if (Serial.available() > 0) { 2227 | settings.chargecurrent2max = Serial.parseInt() * 10; 2228 | menuload = 1; 2229 | incomingByte = 'e'; 2230 | } 2231 | break; 2232 | 2233 | case '5': //1 Over Voltage Setpoint 2234 | settings.chargertype = settings.chargertype + 1; 2235 | if (settings.chargertype > 6) { 2236 | settings.chargertype = 0; 2237 | } 2238 | menuload = 1; 2239 | incomingByte = 'e'; 2240 | break; 2241 | 2242 | case '6': 2243 | if (Serial.available() > 0) { 2244 | settings.chargerspd = Serial.parseInt(); 2245 | menuload = 1; 2246 | incomingByte = 'e'; 2247 | } 2248 | break; 2249 | case '8': 2250 | if (settings.ChargerDirect == 1) { 2251 | settings.ChargerDirect = 0; 2252 | menuload = 1; 2253 | incomingByte = 'e'; 2254 | } else { 2255 | settings.ChargerDirect = 1; 2256 | menuload = 1; 2257 | incomingByte = 'e'; 2258 | } 2259 | break; 2260 | 2261 | case '9': 2262 | if (Serial.available() > 0) { 2263 | settings.ChargeTSetpoint = Serial.parseInt(); 2264 | menuload = 1; 2265 | incomingByte = 'e'; 2266 | } 2267 | break; 2268 | 2269 | case 'b': 2270 | if (Serial.available() > 0) { 2271 | settings.chargereff = Serial.parseInt(); 2272 | menuload = 1; 2273 | incomingByte = 'e'; 2274 | } 2275 | break; 2276 | 2277 | case 'c': 2278 | if (Serial.available() > 0) { 2279 | settings.chargerACv = Serial.parseInt(); 2280 | menuload = 1; 2281 | incomingByte = 'e'; 2282 | } 2283 | break; 2284 | } 2285 | } 2286 | 2287 | if (menuload == 5) { 2288 | switch (incomingByte) { 2289 | case '1': 2290 | if (Serial.available() > 0) { 2291 | settings.Pretime = Serial.parseInt(); 2292 | menuload = 1; 2293 | incomingByte = 'k'; 2294 | } 2295 | break; 2296 | 2297 | case '2': 2298 | if (Serial.available() > 0) { 2299 | settings.Precurrent = Serial.parseInt(); 2300 | menuload = 1; 2301 | incomingByte = 'k'; 2302 | } 2303 | break; 2304 | 2305 | case '3': 2306 | if (Serial.available() > 0) { 2307 | settings.conthold = Serial.parseInt(); 2308 | menuload = 1; 2309 | incomingByte = 'k'; 2310 | } 2311 | break; 2312 | 2313 | case '4': 2314 | if (Serial.available() > 0) { 2315 | settings.gaugelow = Serial.parseInt(); 2316 | gaugedebug = 2; 2317 | gaugeupdate(); 2318 | menuload = 1; 2319 | incomingByte = 'k'; 2320 | } 2321 | break; 2322 | 2323 | case '5': 2324 | if (Serial.available() > 0) { 2325 | settings.gaugehigh = Serial.parseInt(); 2326 | gaugedebug = 3; 2327 | gaugeupdate(); 2328 | menuload = 1; 2329 | incomingByte = 'k'; 2330 | } 2331 | break; 2332 | 2333 | case '6': 2334 | settings.tripcont = !settings.tripcont; 2335 | if (settings.tripcont > 1) { 2336 | settings.tripcont = 0; 2337 | } 2338 | menuload = 1; 2339 | incomingByte = 'k'; 2340 | break; 2341 | 2342 | case 113: //q to go back to main menu 2343 | gaugedebug = 0; 2344 | menuload = 0; 2345 | incomingByte = 115; 2346 | break; 2347 | } 2348 | } 2349 | 2350 | if (menuload == 3) { 2351 | switch (incomingByte) { 2352 | case 113: //q to go back to main menu 2353 | 2354 | menuload = 0; 2355 | incomingByte = 115; 2356 | break; 2357 | 2358 | case 'f': //f factory settings 2359 | loadSettings(); 2360 | SERIALCONSOLE.println(" "); 2361 | SERIALCONSOLE.println(" "); 2362 | SERIALCONSOLE.println(" "); 2363 | SERIALCONSOLE.println(" Coded Settings Loaded "); 2364 | SERIALCONSOLE.println(" "); 2365 | menuload = 1; 2366 | incomingByte = 'b'; 2367 | break; 2368 | 2369 | case 'r': //r for reset 2370 | SOCreset = 1; 2371 | SERIALCONSOLE.println(" "); 2372 | SERIALCONSOLE.print(" mAh Reset "); 2373 | SERIALCONSOLE.println(" "); 2374 | menuload = 1; 2375 | incomingByte = 'b'; 2376 | break; 2377 | 2378 | 2379 | case '1': //1 Over Voltage Setpoint 2380 | if (Serial.available() > 0) { 2381 | settings.OverVSetpoint = Serial.parseInt(); 2382 | settings.OverVSetpoint = settings.OverVSetpoint / 1000; 2383 | menuload = 1; 2384 | incomingByte = 'b'; 2385 | } 2386 | break; 2387 | 2388 | case 'g': 2389 | if (Serial.available() > 0) { 2390 | settings.StoreVsetpoint = Serial.parseInt(); 2391 | settings.StoreVsetpoint = settings.StoreVsetpoint / 1000; 2392 | menuload = 1; 2393 | incomingByte = 'b'; 2394 | } 2395 | 2396 | case 'h': 2397 | if (Serial.available() > 0) { 2398 | settings.DisTaper = Serial.parseInt(); 2399 | settings.DisTaper = settings.DisTaper / 1000; 2400 | menuload = 1; 2401 | incomingByte = 'b'; 2402 | } 2403 | 2404 | 2405 | case 'j': 2406 | if (Serial.available() > 0) { 2407 | settings.DisTSetpoint = Serial.parseInt(); 2408 | menuload = 1; 2409 | incomingByte = 'b'; 2410 | } 2411 | break; 2412 | 2413 | case 'b': 2414 | if (Serial.available() > 0) { 2415 | settings.socvolt[0] = Serial.parseInt(); 2416 | menuload = 1; 2417 | incomingByte = 'b'; 2418 | } 2419 | break; 2420 | 2421 | 2422 | case 'c': 2423 | if (Serial.available() > 0) { 2424 | settings.socvolt[1] = Serial.parseInt(); 2425 | menuload = 1; 2426 | incomingByte = 'b'; 2427 | } 2428 | break; 2429 | 2430 | case 'd': 2431 | if (Serial.available() > 0) { 2432 | settings.socvolt[2] = Serial.parseInt(); 2433 | menuload = 1; 2434 | incomingByte = 'b'; 2435 | } 2436 | break; 2437 | 2438 | case 'e': 2439 | if (Serial.available() > 0) { 2440 | settings.socvolt[3] = Serial.parseInt(); 2441 | menuload = 1; 2442 | incomingByte = 'b'; 2443 | } 2444 | break; 2445 | 2446 | case 'k': //Discharge Voltage hysteresis 2447 | if (Serial.available() > 0) { 2448 | settings.DischHys = Serial.parseInt(); 2449 | settings.DischHys = settings.DischHys / 1000; 2450 | menuload = 1; 2451 | incomingByte = 'b'; 2452 | } 2453 | break; 2454 | 2455 | 2456 | case 'x': //Discharge Voltage hysteresis 2457 | settings.CSCvariant++; 2458 | if (settings.CSCvariant > 1) { 2459 | settings.CSCvariant = 0; 2460 | } 2461 | menuload = 1; 2462 | incomingByte = 'b'; 2463 | break; 2464 | 2465 | case '9': //Discharge Voltage Setpoint 2466 | if (Serial.available() > 0) { 2467 | settings.DischVsetpoint = Serial.parseInt(); 2468 | settings.DischVsetpoint = settings.DischVsetpoint / 1000; 2469 | menuload = 1; 2470 | incomingByte = 'b'; 2471 | } 2472 | break; 2473 | 2474 | case '0': //c Pstrings 2475 | if (Serial.available() > 0) { 2476 | settings.Pstrings = Serial.parseInt(); 2477 | menuload = 1; 2478 | incomingByte = 'b'; 2479 | bms.setPstrings(settings.Pstrings); 2480 | } 2481 | break; 2482 | 2483 | case 'a': // 2484 | if (Serial.available() > 0) { 2485 | settings.Scells = Serial.parseInt(); 2486 | menuload = 1; 2487 | incomingByte = 'b'; 2488 | } 2489 | break; 2490 | 2491 | case '2': //2 Under Voltage Setpoint 2492 | if (Serial.available() > 0) { 2493 | settings.UnderVSetpoint = Serial.parseInt(); 2494 | settings.UnderVSetpoint = settings.UnderVSetpoint / 1000; 2495 | menuload = 1; 2496 | incomingByte = 'b'; 2497 | } 2498 | break; 2499 | 2500 | case '3': //3 Over Temperature Setpoint 2501 | if (Serial.available() > 0) { 2502 | settings.OverTSetpoint = Serial.parseInt(); 2503 | menuload = 1; 2504 | incomingByte = 'b'; 2505 | } 2506 | break; 2507 | 2508 | case '4': //4 Udner Temperature Setpoint 2509 | if (Serial.available() > 0) { 2510 | settings.UnderTSetpoint = Serial.parseInt(); 2511 | menuload = 1; 2512 | incomingByte = 'b'; 2513 | } 2514 | break; 2515 | 2516 | case '5': //5 Balance Voltage Setpoint 2517 | if (Serial.available() > 0) { 2518 | settings.balanceVoltage = Serial.parseInt(); 2519 | settings.balanceVoltage = settings.balanceVoltage / 1000; 2520 | menuload = 1; 2521 | incomingByte = 'b'; 2522 | } 2523 | break; 2524 | 2525 | case '6': //6 Balance Voltage Hystersis 2526 | if (Serial.available() > 0) { 2527 | settings.balanceHyst = Serial.parseInt(); 2528 | settings.balanceHyst = settings.balanceHyst / 1000; 2529 | menuload = 1; 2530 | incomingByte = 'b'; 2531 | } 2532 | break; 2533 | 2534 | case '7': //7 Battery Capacity inAh 2535 | if (Serial.available() > 0) { 2536 | settings.CAP = Serial.parseInt(); 2537 | menuload = 1; 2538 | incomingByte = 'b'; 2539 | } 2540 | break; 2541 | 2542 | case '8': // discurrent in A 2543 | if (Serial.available() > 0) { 2544 | settings.discurrentmax = Serial.parseInt() * 10; 2545 | menuload = 1; 2546 | incomingByte = 'b'; 2547 | } 2548 | break; 2549 | } 2550 | } 2551 | 2552 | if (menuload == 1) { 2553 | switch (incomingByte) { 2554 | case 'R': //restart 2555 | CPU_REBOOT; 2556 | break; 2557 | 2558 | case 'i': //Ignore Value Settings 2559 | while (Serial.available()) { 2560 | Serial.read(); 2561 | } 2562 | SERIALCONSOLE.println(); 2563 | SERIALCONSOLE.println(); 2564 | SERIALCONSOLE.println(); 2565 | SERIALCONSOLE.println(); 2566 | SERIALCONSOLE.println(); 2567 | SERIALCONSOLE.println("Ignore Value Settings"); 2568 | SERIALCONSOLE.print("1 - Temp Sensor Setting:"); 2569 | SERIALCONSOLE.println(settings.IgnoreTemp); 2570 | SERIALCONSOLE.print("2 - Voltage Under Which To Ignore Cells:"); 2571 | SERIALCONSOLE.print(settings.IgnoreVolt * 1000, 0); 2572 | SERIALCONSOLE.println("mV"); 2573 | SERIALCONSOLE.print("4 - Temp Offset Setting:"); 2574 | SERIALCONSOLE.println(settings.TempOff); 2575 | SERIALCONSOLE.println("q - Go back to menu"); 2576 | menuload = 8; 2577 | break; 2578 | 2579 | case 'e': //Charging settings 2580 | while (Serial.available()) { 2581 | Serial.read(); 2582 | } 2583 | SERIALCONSOLE.println(); 2584 | SERIALCONSOLE.println(); 2585 | SERIALCONSOLE.println(); 2586 | SERIALCONSOLE.println(); 2587 | SERIALCONSOLE.println(); 2588 | SERIALCONSOLE.println("Charging Settings"); 2589 | SERIALCONSOLE.print("1 - Cell Charge Voltage Limit Setpoint: "); 2590 | SERIALCONSOLE.print(settings.ChargeVsetpoint * 1000, 0); 2591 | SERIALCONSOLE.println("mV"); 2592 | SERIALCONSOLE.print("2 - Charge Hystersis: "); 2593 | SERIALCONSOLE.print(settings.ChargeHys * 1000, 0); 2594 | SERIALCONSOLE.println("mV"); 2595 | if (settings.chargertype > 0) { 2596 | SERIALCONSOLE.print("3 - Pack Max Charge Current: "); 2597 | SERIALCONSOLE.print(settings.chargecurrentmax * 0.1); 2598 | SERIALCONSOLE.println("A"); 2599 | SERIALCONSOLE.print("4- Pack End of Charge Current: "); 2600 | SERIALCONSOLE.print(settings.chargecurrentend * 0.1); 2601 | SERIALCONSOLE.println("A"); 2602 | } 2603 | SERIALCONSOLE.print("5- Charger Type: "); 2604 | switch (settings.chargertype) { 2605 | case 0: 2606 | SERIALCONSOLE.print("Relay Control"); 2607 | break; 2608 | case 1: 2609 | SERIALCONSOLE.print("Brusa NLG5xx"); 2610 | break; 2611 | case 2: 2612 | SERIALCONSOLE.print("Volt Charger"); 2613 | break; 2614 | case 3: 2615 | SERIALCONSOLE.print("Eltek Charger"); 2616 | break; 2617 | case 4: 2618 | SERIALCONSOLE.print("Elcon Charger"); 2619 | break; 2620 | case 5: 2621 | SERIALCONSOLE.print("Victron/SMA"); 2622 | break; 2623 | case 6: 2624 | SERIALCONSOLE.print("Coda"); 2625 | break; 2626 | } 2627 | SERIALCONSOLE.println(); 2628 | if (settings.chargertype > 0) { 2629 | SERIALCONSOLE.print("6- Charger Can Msg Spd: "); 2630 | SERIALCONSOLE.print(settings.chargerspd); 2631 | SERIALCONSOLE.println("mS"); 2632 | } 2633 | SERIALCONSOLE.print("8 - Charger HV Connection: "); 2634 | switch (settings.ChargerDirect) { 2635 | case 0: 2636 | SERIALCONSOLE.print(" Behind Contactors"); 2637 | break; 2638 | case 1: 2639 | SERIALCONSOLE.print("Direct To Battery HV"); 2640 | break; 2641 | } 2642 | SERIALCONSOLE.println(); 2643 | SERIALCONSOLE.print("9 - Charge Current derate Low: "); 2644 | SERIALCONSOLE.print(settings.ChargeTSetpoint); 2645 | SERIALCONSOLE.println(" C"); 2646 | if (settings.chargertype > 0) { 2647 | SERIALCONSOLE.print("a - Alternate Pack Max Charge Current: "); 2648 | SERIALCONSOLE.print(settings.chargecurrent2max * 0.1); 2649 | SERIALCONSOLE.println("A"); 2650 | SERIALCONSOLE.print("b - Charger AC to DC effiecency: "); 2651 | SERIALCONSOLE.print(settings.chargereff); 2652 | SERIALCONSOLE.println("%"); 2653 | SERIALCONSOLE.print("c - Charger AC Voltage: "); 2654 | SERIALCONSOLE.print(settings.chargerACv); 2655 | SERIALCONSOLE.println("VAC"); 2656 | } 2657 | SERIALCONSOLE.println("q - Go back to menu"); 2658 | menuload = 6; 2659 | break; 2660 | 2661 | 2662 | case 'a': //Alarm and Warning settings 2663 | while (Serial.available()) { 2664 | Serial.read(); 2665 | } 2666 | SERIALCONSOLE.println(); 2667 | SERIALCONSOLE.println(); 2668 | SERIALCONSOLE.println(); 2669 | SERIALCONSOLE.println(); 2670 | SERIALCONSOLE.println(); 2671 | SERIALCONSOLE.println("Alarm and Warning Settings Menu"); 2672 | SERIALCONSOLE.print("1 - Voltage Warning Offset: "); 2673 | SERIALCONSOLE.print(settings.WarnOff * 1000, 0); 2674 | SERIALCONSOLE.println("mV"); 2675 | SERIALCONSOLE.print("2 - Cell Voltage Difference Alarm: "); 2676 | SERIALCONSOLE.print(settings.CellGap * 1000, 0); 2677 | SERIALCONSOLE.println("mV"); 2678 | SERIALCONSOLE.print("3 - Temp Warning Offset: "); 2679 | SERIALCONSOLE.print(settings.WarnToff); 2680 | SERIALCONSOLE.println(" C"); 2681 | //SERIALCONSOLE.print("4 - Temp Warning delay: "); 2682 | //SERIALCONSOLE.print(settings.UnderDur); 2683 | //SERIALCONSOLE.println(" mS"); 2684 | SERIALCONSOLE.print("4 - Over and Under Voltage Delay: "); 2685 | SERIALCONSOLE.print(settings.triptime); 2686 | SERIALCONSOLE.println(" mS"); 2687 | menuload = 7; 2688 | break; 2689 | 2690 | case 'k': //contactor settings 2691 | while (Serial.available()) { 2692 | Serial.read(); 2693 | } 2694 | SERIALCONSOLE.println(); 2695 | SERIALCONSOLE.println(); 2696 | SERIALCONSOLE.println(); 2697 | SERIALCONSOLE.println(); 2698 | SERIALCONSOLE.println(); 2699 | SERIALCONSOLE.println("Contactor and Gauge Settings Menu"); 2700 | SERIALCONSOLE.print("1 - PreCharge Timer: "); 2701 | SERIALCONSOLE.print(settings.Pretime); 2702 | SERIALCONSOLE.println("mS"); 2703 | SERIALCONSOLE.print("2 - PreCharge Finish Current: "); 2704 | SERIALCONSOLE.print(settings.Precurrent); 2705 | SERIALCONSOLE.println(" mA"); 2706 | SERIALCONSOLE.print("3 - PWM contactor Hold 0-255 :"); 2707 | SERIALCONSOLE.println(settings.conthold); 2708 | SERIALCONSOLE.print("4 - PWM for Gauge Low 0-255 :"); 2709 | SERIALCONSOLE.println(settings.gaugelow); 2710 | SERIALCONSOLE.print("5 - PWM for Gauge High 0-255 :"); 2711 | SERIALCONSOLE.println(settings.gaugehigh); 2712 | if (settings.ESSmode == 1) { 2713 | SERIALCONSOLE.print("6 - ESS Main Contactor or Trip :"); 2714 | if (settings.tripcont == 0) { 2715 | SERIALCONSOLE.println("Trip Shunt"); 2716 | } else { 2717 | SERIALCONSOLE.println("Main Contactor and Precharge"); 2718 | } 2719 | } 2720 | menuload = 5; 2721 | break; 2722 | 2723 | case 113: //q to go back to main menu 2724 | EEPROM.put(0, settings); //save all change to eeprom 2725 | menuload = 0; 2726 | debug = 1; 2727 | break; 2728 | case 'd': //d for debug settings 2729 | while (Serial.available()) { 2730 | Serial.read(); 2731 | } 2732 | SERIALCONSOLE.println(); 2733 | SERIALCONSOLE.println(); 2734 | SERIALCONSOLE.println(); 2735 | SERIALCONSOLE.println(); 2736 | SERIALCONSOLE.println(); 2737 | SERIALCONSOLE.println("Debug Settings Menu"); 2738 | SERIALCONSOLE.println("Toggle on/off"); 2739 | SERIALCONSOLE.print("1 - Can Debug :"); 2740 | SERIALCONSOLE.println(candebug); 2741 | SERIALCONSOLE.print("2 - Current Debug :"); 2742 | SERIALCONSOLE.println(debugCur); 2743 | SERIALCONSOLE.print("3 - Output Check :"); 2744 | SERIALCONSOLE.println(outputcheck); 2745 | SERIALCONSOLE.print("4 - Input Check :"); 2746 | SERIALCONSOLE.println(inputcheck); 2747 | SERIALCONSOLE.print("5 - ESS mode :"); 2748 | SERIALCONSOLE.println(settings.ESSmode); 2749 | SERIALCONSOLE.print("6 - Cells Present Reset :"); 2750 | SERIALCONSOLE.println(cellspresent); 2751 | SERIALCONSOLE.print("7 - Gauge Debug :"); 2752 | SERIALCONSOLE.println(gaugedebug); 2753 | SERIALCONSOLE.print("8 - CSV Output :"); 2754 | SERIALCONSOLE.println(CSVdebug); 2755 | SERIALCONSOLE.print("9 - Decimal Places to Show :"); 2756 | SERIALCONSOLE.println(debugdigits); 2757 | SERIALCONSOLE.print("b - balance duration :"); 2758 | SERIALCONSOLE.print(settings.balanceDuty); 2759 | SERIALCONSOLE.println(" S time before starting is 60s"); 2760 | 2761 | ///Testing ID assignment/// 2762 | SERIALCONSOLE.print("y - NextID :"); 2763 | SERIALCONSOLE.print(NextID); 2764 | SERIALCONSOLE.println(); 2765 | SERIALCONSOLE.print("x - wipe CSC ids"); 2766 | SERIALCONSOLE.println(""); 2767 | /////////// 2768 | 2769 | SERIALCONSOLE.println("r - reset balance debug"); 2770 | SERIALCONSOLE.println("q - Go back to menu"); 2771 | menuload = 4; 2772 | break; 2773 | 2774 | case 99: //c for calibrate zero offset 2775 | while (Serial.available()) { 2776 | Serial.read(); 2777 | } 2778 | SERIALCONSOLE.println(); 2779 | SERIALCONSOLE.println(); 2780 | SERIALCONSOLE.println(); 2781 | SERIALCONSOLE.println(); 2782 | SERIALCONSOLE.println(); 2783 | SERIALCONSOLE.println("Current Sensor Calibration Menu"); 2784 | SERIALCONSOLE.println("c - To calibrate sensor offset"); 2785 | SERIALCONSOLE.print("s - Current Sensor Type : "); 2786 | switch (settings.cursens) { 2787 | case Analoguedual: 2788 | SERIALCONSOLE.println(" Analogue Dual Current Sensor "); 2789 | break; 2790 | case Analoguesing: 2791 | SERIALCONSOLE.println(" Analogue Single Current Sensor "); 2792 | break; 2793 | case Canbus: 2794 | SERIALCONSOLE.println(" Canbus Current Sensor "); 2795 | break; 2796 | default: 2797 | SERIALCONSOLE.println("Undefined"); 2798 | break; 2799 | } 2800 | SERIALCONSOLE.print("1 - invert current :"); 2801 | SERIALCONSOLE.println(settings.invertcur); 2802 | SERIALCONSOLE.print("2 - Pure Voltage based SOC :"); 2803 | SERIALCONSOLE.println(settings.voltsoc); 2804 | SERIALCONSOLE.print("3 - Current Multiplication :"); 2805 | SERIALCONSOLE.println(settings.ncur); 2806 | if (settings.cursens == Analoguesing || settings.cursens == Analoguedual) { 2807 | SERIALCONSOLE.print("4 - Analogue Low Range Conv:"); 2808 | SERIALCONSOLE.print(settings.convlow * 0.01, 2); 2809 | SERIALCONSOLE.println(" mV/A"); 2810 | } 2811 | if (settings.cursens == Analoguedual) { 2812 | SERIALCONSOLE.print("5 - Analogue High Range Conv:"); 2813 | SERIALCONSOLE.print(settings.convhigh * 0.01, 2); 2814 | SERIALCONSOLE.println(" mV/A"); 2815 | } 2816 | if (settings.cursens == Analoguesing || settings.cursens == Analoguedual) { 2817 | SERIALCONSOLE.print("6 - Current Sensor Deadband:"); 2818 | SERIALCONSOLE.print(settings.CurDead); 2819 | SERIALCONSOLE.println(" mV"); 2820 | } 2821 | if (settings.cursens == Analoguedual) { 2822 | 2823 | SERIALCONSOLE.print("8 - Current Channel ChangeOver:"); 2824 | SERIALCONSOLE.print(settings.changecur * 0.001); 2825 | SERIALCONSOLE.println(" A"); 2826 | } 2827 | 2828 | if (settings.cursens == Canbus) { 2829 | SERIALCONSOLE.print("7 -Can Current Sensor :"); 2830 | if (settings.curcan == LemCAB300) { 2831 | SERIALCONSOLE.println(" LEM CAB300/500 series "); 2832 | } else if (settings.curcan == LemCAB500) { 2833 | SERIALCONSOLE.println(" LEM CAB500 Special "); 2834 | } else if (settings.curcan == IsaScale) { 2835 | SERIALCONSOLE.println(" IsaScale IVT-S "); 2836 | } 2837 | } 2838 | SERIALCONSOLE.println("q - Go back to menu"); 2839 | menuload = 2; 2840 | break; 2841 | 2842 | case 98: //c for calibrate zero offset 2843 | while (Serial.available()) { 2844 | Serial.read(); 2845 | } 2846 | SERIALCONSOLE.println(); 2847 | SERIALCONSOLE.println(); 2848 | SERIALCONSOLE.println(); 2849 | SERIALCONSOLE.println(); 2850 | SERIALCONSOLE.println(); 2851 | SERIALCONSOLE.println("Battery Settings Menu"); 2852 | SERIALCONSOLE.println("r - Reset AH counter"); 2853 | SERIALCONSOLE.println("f - Reset to Coded Settings"); 2854 | SERIALCONSOLE.println("q - Go back to menu"); 2855 | SERIALCONSOLE.println(); 2856 | SERIALCONSOLE.println(); 2857 | SERIALCONSOLE.print("1 - Cell Over Voltage Setpoint: "); 2858 | SERIALCONSOLE.print(settings.OverVSetpoint * 1000, 0); 2859 | SERIALCONSOLE.print("mV"); 2860 | SERIALCONSOLE.println(" "); 2861 | SERIALCONSOLE.print("2 - Cell Under Voltage Setpoint: "); 2862 | SERIALCONSOLE.print(settings.UnderVSetpoint * 1000, 0); 2863 | SERIALCONSOLE.print("mV"); 2864 | SERIALCONSOLE.println(" "); 2865 | SERIALCONSOLE.print("3 - Over Temperature Setpoint: "); 2866 | SERIALCONSOLE.print(settings.OverTSetpoint); 2867 | SERIALCONSOLE.print("C"); 2868 | SERIALCONSOLE.println(" "); 2869 | SERIALCONSOLE.print("4 - Under Temperature Setpoint: "); 2870 | SERIALCONSOLE.print(settings.UnderTSetpoint); 2871 | SERIALCONSOLE.print("C"); 2872 | SERIALCONSOLE.println(" "); 2873 | SERIALCONSOLE.print("5 - Cell Balance Voltage Setpoint: "); 2874 | SERIALCONSOLE.print(settings.balanceVoltage * 1000, 0); 2875 | SERIALCONSOLE.print("mV"); 2876 | SERIALCONSOLE.println(" "); 2877 | SERIALCONSOLE.print("6 - Balance Voltage Hystersis: "); 2878 | SERIALCONSOLE.print(settings.balanceHyst * 1000, 0); 2879 | SERIALCONSOLE.print("mV"); 2880 | SERIALCONSOLE.println(" "); 2881 | SERIALCONSOLE.print("7 - Ah Battery Capacity: "); 2882 | SERIALCONSOLE.print(settings.CAP); 2883 | SERIALCONSOLE.print("Ah"); 2884 | SERIALCONSOLE.println(" "); 2885 | SERIALCONSOLE.print("8 - Pack Max Discharge: "); 2886 | SERIALCONSOLE.print(settings.discurrentmax * 0.1); 2887 | SERIALCONSOLE.print("A"); 2888 | SERIALCONSOLE.println(" "); 2889 | SERIALCONSOLE.print("9 - Cell Discharge Voltage Limit Setpoint: "); 2890 | SERIALCONSOLE.print(settings.DischVsetpoint * 1000, 0); 2891 | SERIALCONSOLE.print("mV"); 2892 | SERIALCONSOLE.println(" "); 2893 | SERIALCONSOLE.print("0 - Slave strings in parallel: "); 2894 | SERIALCONSOLE.print(settings.Pstrings); 2895 | SERIALCONSOLE.println(" "); 2896 | SERIALCONSOLE.print("a - Cells in Series per String: "); 2897 | SERIALCONSOLE.print(settings.Scells); 2898 | SERIALCONSOLE.println(" "); 2899 | SERIALCONSOLE.print("b - setpoint 1: "); 2900 | SERIALCONSOLE.print(settings.socvolt[0]); 2901 | SERIALCONSOLE.print("mV"); 2902 | SERIALCONSOLE.println(" "); 2903 | SERIALCONSOLE.print("c - SOC setpoint 1:"); 2904 | SERIALCONSOLE.print(settings.socvolt[1]); 2905 | SERIALCONSOLE.print("%"); 2906 | SERIALCONSOLE.println(" "); 2907 | SERIALCONSOLE.print("d - setpoint 2: "); 2908 | SERIALCONSOLE.print(settings.socvolt[2]); 2909 | SERIALCONSOLE.print("mV"); 2910 | SERIALCONSOLE.println(" "); 2911 | SERIALCONSOLE.print("e - SOC setpoint 2: "); 2912 | SERIALCONSOLE.print(settings.socvolt[3]); 2913 | SERIALCONSOLE.print("%"); 2914 | SERIALCONSOLE.println(" "); 2915 | SERIALCONSOLE.print("g - Storage Setpoint: "); 2916 | SERIALCONSOLE.print(settings.StoreVsetpoint * 1000, 0); 2917 | SERIALCONSOLE.print("mV"); 2918 | SERIALCONSOLE.println(" "); 2919 | SERIALCONSOLE.print("h - Discharge Current Taper Offset: "); 2920 | SERIALCONSOLE.print(settings.DisTaper * 1000, 0); 2921 | SERIALCONSOLE.print("mV"); 2922 | SERIALCONSOLE.println(" "); 2923 | SERIALCONSOLE.print("j - Discharge Current Temperature Derate : "); 2924 | SERIALCONSOLE.print(settings.DisTSetpoint); 2925 | SERIALCONSOLE.print("C"); 2926 | SERIALCONSOLE.println(""); 2927 | SERIALCONSOLE.print("k - Cell Discharge Voltage Hysteresis: "); 2928 | SERIALCONSOLE.print(settings.DischHys * 1000, 0); 2929 | SERIALCONSOLE.print("mV"); 2930 | SERIALCONSOLE.println(" "); 2931 | 2932 | SERIALCONSOLE.print("x - CSC Variant Used: "); 2933 | if (settings.CSCvariant == BmwI3) { 2934 | SERIALCONSOLE.print("Bmw I3"); 2935 | } 2936 | if (settings.CSCvariant == MiniE) { 2937 | SERIALCONSOLE.print("Mini-E"); 2938 | } 2939 | 2940 | SERIALCONSOLE.println(" "); 2941 | 2942 | 2943 | 2944 | SERIALCONSOLE.println(); 2945 | menuload = 3; 2946 | break; 2947 | 2948 | default: 2949 | // if nothing else matches, do the default 2950 | // default is optional 2951 | break; 2952 | } 2953 | } 2954 | 2955 | if (incomingByte == 115 && menuload == 0) { 2956 | SERIALCONSOLE.println(); 2957 | SERIALCONSOLE.println("MENU"); 2958 | SERIALCONSOLE.println("Debugging Paused"); 2959 | SERIALCONSOLE.print("Firmware Version : "); 2960 | SERIALCONSOLE.println(firmver); 2961 | SERIALCONSOLE.println("b - Battery Settings"); 2962 | SERIALCONSOLE.println("a - Alarm and Warning Settings"); 2963 | SERIALCONSOLE.println("e - Charging Settings"); 2964 | SERIALCONSOLE.println("c - Current Sensor Calibration"); 2965 | SERIALCONSOLE.println("k - Contactor and Gauge Settings"); 2966 | SERIALCONSOLE.println("i - Ignore Value Settings"); 2967 | SERIALCONSOLE.println("d - Debug Settings"); 2968 | SERIALCONSOLE.println("R - Restart BMS"); 2969 | SERIALCONSOLE.println("q - exit menu"); 2970 | debug = 0; 2971 | menuload = 1; 2972 | } 2973 | } 2974 | 2975 | void canread() { 2976 | Can0.read(inMsg); 2977 | // Read data: len = data length, buf = data byte(s) 2978 | if (settings.cursens == Canbus) { 2979 | if (settings.curcan == 1) { 2980 | switch (inMsg.id) { 2981 | case 0x3c1: 2982 | CAB500(); 2983 | break; 2984 | 2985 | case 0x3c2: 2986 | CAB300(); 2987 | break; 2988 | 2989 | default: 2990 | break; 2991 | } 2992 | } 2993 | if (settings.curcan == 2) { 2994 | switch (inMsg.id) { 2995 | case 0x3c1: 2996 | CAB500(); 2997 | break; 2998 | 2999 | case 0x3c2: 3000 | CAB500(); 3001 | break; 3002 | 3003 | default: 3004 | break; 3005 | } 3006 | } 3007 | if (settings.curcan == 3) { 3008 | switch (inMsg.id) { 3009 | case 0x521: // 3010 | CANmilliamps = (long)((inMsg.buf[2] << 24) | (inMsg.buf[3] << 16) | (inMsg.buf[4] << 8) | (inMsg.buf[5])); 3011 | RawCur = CANmilliamps; 3012 | getcurrent(); 3013 | break; 3014 | 3015 | case 0x522: // 3016 | voltage1 = (long)((inMsg.buf[2] << 24) | (inMsg.buf[3] << 16) | (inMsg.buf[4] << 8) | (inMsg.buf[5])); 3017 | break; 3018 | 3019 | case 0x523: // 3020 | voltage2 = (long)((inMsg.buf[2] << 24) | (inMsg.buf[3] << 16) | (inMsg.buf[4] << 8) | (inMsg.buf[5])); 3021 | break; 3022 | 3023 | default: 3024 | break; 3025 | } 3026 | } 3027 | } 3028 | 3029 | 3030 | //ID not assigned// 3031 | if (inMsg.id == 0xF0) { 3032 | Unassigned++; 3033 | Serial.print(millis()); 3034 | if ((inMsg.id & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits) 3035 | sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (inMsg.id & 0x1FFFFFFF), inMsg.len); 3036 | else 3037 | sprintf(msgString, ",0x%.3lX,false,%1d", inMsg.id, inMsg.len); 3038 | 3039 | Serial.print(msgString); 3040 | 3041 | if ((inMsg.id & 0x40000000) == 0x40000000) { // Determine if message is a remote request frame. 3042 | sprintf(msgString, " REMOTE REQUEST FRAME"); 3043 | Serial.print(msgString); 3044 | } else { 3045 | for (byte i = 0; i < inMsg.len; i++) { 3046 | sprintf(msgString, ", 0x%.2X", inMsg.buf[i]); 3047 | DMC[i] = inMsg.buf[i]; 3048 | Serial.print(msgString); 3049 | } 3050 | } 3051 | 3052 | Serial.println(); 3053 | for (byte i = 0; i < 8; i++) { 3054 | Serial.print(DMC[i], HEX); 3055 | Serial.print("|"); 3056 | } 3057 | Serial.println(); 3058 | } 3059 | //// 3060 | 3061 | if (inMsg.id > 0x99 && inMsg.id < 0x180) //do BMS magic if ids are ones identified to be modules 3062 | { 3063 | if (candebug == 1 && debug == 1) { 3064 | bms.decodecan(inMsg, 1); //do BMS if ids are ones identified to be modules 3065 | } else { 3066 | bms.decodecan(inMsg, 0); //do BMS if ids are ones identified to be modules 3067 | } 3068 | } 3069 | if ((inMsg.id & 0xFF0) == 0x180) // Determine if ID is standard (11 bits) or extended (29 bits) 3070 | { 3071 | if (candebug == 1 && debug == 1) { 3072 | bms.decodetemp(inMsg, 1, settings.CSCvariant); 3073 | } else { 3074 | bms.decodetemp(inMsg, 0, settings.CSCvariant); 3075 | } 3076 | } 3077 | if (debug == 1) { 3078 | if (candebug == 1) { 3079 | Serial.print(millis()); 3080 | if ((inMsg.id & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits) 3081 | sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (inMsg.id & 0x1FFFFFFF), inMsg.len); 3082 | else 3083 | sprintf(msgString, ",0x%.3lX,false,%1d", inMsg.id, inMsg.len); 3084 | 3085 | Serial.print(msgString); 3086 | 3087 | if ((inMsg.id & 0x40000000) == 0x40000000) { // Determine if message is a remote request frame. 3088 | sprintf(msgString, " REMOTE REQUEST FRAME"); 3089 | Serial.print(msgString); 3090 | } else { 3091 | for (byte i = 0; i < inMsg.len; i++) { 3092 | sprintf(msgString, ", 0x%.2X", inMsg.buf[i]); 3093 | Serial.print(msgString); 3094 | } 3095 | } 3096 | 3097 | Serial.println(); 3098 | } 3099 | } 3100 | } 3101 | 3102 | void CAB300() { 3103 | for (int i = 0; i < 4; i++) { 3104 | inbox = (inbox << 8) | inMsg.buf[i]; 3105 | } 3106 | CANmilliamps = inbox; 3107 | if (CANmilliamps > 0x80000000) { 3108 | CANmilliamps -= 0x80000000; 3109 | } else { 3110 | CANmilliamps = (0x80000000 - CANmilliamps) * -1; 3111 | } 3112 | if (settings.cursens == Canbus) { 3113 | RawCur = CANmilliamps; 3114 | getcurrent(); 3115 | } 3116 | if (candebug == 1) { 3117 | Serial.println(); 3118 | Serial.print(CANmilliamps); 3119 | Serial.print("mA "); 3120 | } 3121 | } 3122 | 3123 | void CAB500() { 3124 | inbox = 0; 3125 | for (int i = 1; i < 4; i++) { 3126 | inbox = (inbox << 8) | inMsg.buf[i]; 3127 | } 3128 | CANmilliamps = inbox; 3129 | if (candebug == 1) { 3130 | Serial.println(); 3131 | Serial.print(CANmilliamps, HEX); 3132 | } 3133 | if (CANmilliamps > 0x800000) { 3134 | CANmilliamps -= 0x800000; 3135 | } else { 3136 | CANmilliamps = (0x800000 - CANmilliamps) * -1; 3137 | } 3138 | if (settings.cursens == Canbus) { 3139 | RawCur = CANmilliamps; 3140 | getcurrent(); 3141 | } 3142 | if (candebug == 1) { 3143 | Serial.println(); 3144 | Serial.print(CANmilliamps); 3145 | Serial.print("mA "); 3146 | } 3147 | } 3148 | 3149 | void currentlimit() { 3150 | if (bmsstatus == Error) { 3151 | discurrent = 0; 3152 | chargecurrent = 0; 3153 | } 3154 | /* 3155 | settings.PulseCh = 600; //Peak Charge current in 0.1A 3156 | settings.PulseChDur = 5000; //Ms of discharge pulse derating 3157 | settings.PulseDi = 600; //Peak Charge current in 0.1A 3158 | settings.PulseDiDur = 5000; //Ms of discharge pulse derating 3159 | */ 3160 | else { 3161 | 3162 | ///Start at no derating/// 3163 | discurrent = settings.discurrentmax; 3164 | 3165 | if (chargecurrentlimit == false) { 3166 | chargecurrent = settings.chargecurrentmax; 3167 | } else { 3168 | chargecurrent = settings.chargecurrent2max; 3169 | } 3170 | 3171 | ///////All hard limits to into zeros 3172 | if (bms.getLowTemperature() < settings.UnderTSetpoint) { 3173 | //discurrent = 0; Request Daniel 3174 | chargecurrent = 0; 3175 | } 3176 | if (bms.getHighTemperature() > settings.OverTSetpoint) { 3177 | discurrent = 0; 3178 | chargecurrent = 0; 3179 | } 3180 | if (bms.getHighCellVolt() > settings.OverVSetpoint) { 3181 | chargecurrent = 0; 3182 | } 3183 | if (bms.getHighCellVolt() > settings.OverVSetpoint) { 3184 | chargecurrent = 0; 3185 | } 3186 | if (bms.getLowCellVolt() < settings.UnderVSetpoint || bms.getLowCellVolt() < settings.DischVsetpoint) { 3187 | discurrent = 0; 3188 | } 3189 | 3190 | 3191 | //Modifying discharge current/// 3192 | 3193 | if (discurrent > 0) { 3194 | //Temperature based/// 3195 | 3196 | if (bms.getHighTemperature() > settings.DisTSetpoint) { 3197 | discurrent = discurrent - map(bms.getHighTemperature(), settings.DisTSetpoint, settings.OverTSetpoint, 0, settings.discurrentmax); 3198 | } 3199 | //Voltagee based/// 3200 | if (bms.getLowCellVolt() < (settings.DischVsetpoint + settings.DisTaper)) { 3201 | discurrent = discurrent - map(bms.getLowCellVolt(), settings.DischVsetpoint, (settings.DischVsetpoint + settings.DisTaper), settings.discurrentmax, 0); 3202 | } 3203 | } 3204 | 3205 | //Modifying Charge current/// 3206 | 3207 | if (chargecurrent > 0) { 3208 | if (chargecurrentlimit == false) { 3209 | //Temperature based/// 3210 | if (bms.getLowTemperature() < settings.ChargeTSetpoint) { 3211 | chargecurrent = chargecurrent - map(bms.getLowTemperature(), settings.UnderTSetpoint, settings.ChargeTSetpoint, settings.chargecurrentmax, 0); 3212 | } 3213 | //Voltagee based/// 3214 | if (storagemode == 1) { 3215 | if (bms.getHighCellVolt() > (settings.StoreVsetpoint - settings.ChargeHys)) { 3216 | chargecurrent = chargecurrent - map(bms.getHighCellVolt(), (settings.StoreVsetpoint - settings.ChargeHys), settings.StoreVsetpoint, settings.chargecurrentend, settings.chargecurrentmax); 3217 | } 3218 | } else { 3219 | if (bms.getHighCellVolt() > (settings.ChargeVsetpoint - settings.ChargeHys)) { 3220 | chargecurrent = chargecurrent - map(bms.getHighCellVolt(), (settings.ChargeVsetpoint - settings.ChargeHys), settings.ChargeVsetpoint, 0, (settings.chargecurrentmax - settings.chargecurrentend)); 3221 | } 3222 | } 3223 | } else { 3224 | //Temperature based/// 3225 | if (bms.getLowTemperature() < settings.ChargeTSetpoint) { 3226 | chargecurrent = chargecurrent - map(bms.getLowTemperature(), settings.UnderTSetpoint, settings.ChargeTSetpoint, settings.chargecurrent2max, 0); 3227 | } 3228 | //Voltagee based/// 3229 | if (storagemode == 1) { 3230 | if (bms.getHighCellVolt() > (settings.StoreVsetpoint - settings.ChargeHys)) { 3231 | chargecurrent = chargecurrent - map(bms.getHighCellVolt(), (settings.StoreVsetpoint - settings.ChargeHys), settings.StoreVsetpoint, settings.chargecurrentend, settings.chargecurrent2max); 3232 | } 3233 | } else { 3234 | if (bms.getHighCellVolt() > (settings.ChargeVsetpoint - settings.ChargeHys)) { 3235 | chargecurrent = chargecurrent - map(bms.getHighCellVolt(), (settings.ChargeVsetpoint - settings.ChargeHys), settings.ChargeVsetpoint, 0, (settings.chargecurrent2max - settings.chargecurrentend)); 3236 | } 3237 | } 3238 | } 3239 | } 3240 | } 3241 | ///No negative currents/// 3242 | 3243 | if (discurrent < 0) { 3244 | discurrent = 0; 3245 | } 3246 | if (chargecurrent < 0) { 3247 | chargecurrent = 0; 3248 | } 3249 | 3250 | //Charge current derate for Control Pilot AC limit 3251 | 3252 | if (accurlim > 0) { 3253 | chargerpower = accurlim * settings.chargerACv * settings.chargereff * 0.01; 3254 | tempchargecurrent = (chargerpower * 10) / (bms.getAvgCellVolt() * settings.Scells); 3255 | 3256 | if (chargecurrent > tempchargecurrent) { 3257 | chargecurrent = tempchargecurrent; 3258 | } 3259 | } 3260 | } 3261 | 3262 | 3263 | void inputdebug() { 3264 | Serial.println(); 3265 | Serial.print("Input: "); 3266 | if (digitalRead(IN1)) { 3267 | Serial.print("1 ON "); 3268 | } else { 3269 | Serial.print("1 OFF "); 3270 | } 3271 | if (digitalRead(IN2)) { 3272 | Serial.print("2 ON "); 3273 | } else { 3274 | Serial.print("2 OFF "); 3275 | } 3276 | if (digitalRead(IN3)) { 3277 | Serial.print("3 ON "); 3278 | } else { 3279 | Serial.print("3 OFF "); 3280 | } 3281 | if (digitalRead(IN4)) { 3282 | Serial.print("4 ON "); 3283 | } else { 3284 | Serial.print("4 OFF "); 3285 | } 3286 | Serial.println(); 3287 | } 3288 | 3289 | void outputdebug() { 3290 | if (outputstate < 5) { 3291 | digitalWrite(OUT1, HIGH); 3292 | digitalWrite(OUT2, HIGH); 3293 | digitalWrite(OUT3, HIGH); 3294 | digitalWrite(OUT4, HIGH); 3295 | analogWrite(OUT5, 255); 3296 | analogWrite(OUT6, 255); 3297 | analogWrite(OUT7, 255); 3298 | analogWrite(OUT8, 255); 3299 | outputstate++; 3300 | } else { 3301 | digitalWrite(OUT1, LOW); 3302 | digitalWrite(OUT2, LOW); 3303 | digitalWrite(OUT3, LOW); 3304 | digitalWrite(OUT4, LOW); 3305 | analogWrite(OUT5, 0); 3306 | analogWrite(OUT6, 0); 3307 | analogWrite(OUT7, 0); 3308 | analogWrite(OUT8, 0); 3309 | outputstate++; 3310 | } 3311 | if (outputstate > 10) { 3312 | outputstate = 0; 3313 | } 3314 | } 3315 | 3316 | void balancing() { 3317 | //Function to control balancing command, to be found 3318 | } 3319 | 3320 | void sendcommand() //Send Can Command to get data from slaves 3321 | { 3322 | ///////module id cycling///////// 3323 | 3324 | if (nextmes == 6) { 3325 | mescycle++; 3326 | nextmes = 0; 3327 | if (testcycle < 4) { 3328 | testcycle++; 3329 | } 3330 | 3331 | if (mescycle == 0xF) { 3332 | mescycle = 0; 3333 | 3334 | if (balancetimer < millis()) { 3335 | balancepauze = 1; 3336 | if (debug == 1) { 3337 | Serial.println(); 3338 | Serial.println("Reset Balance Timer"); 3339 | Serial.println(); 3340 | } 3341 | balancetimer = millis() + ((settings.balanceDuty + 60) * 1000); 3342 | } else { 3343 | balancepauze = 0; 3344 | } 3345 | } 3346 | } 3347 | if (balancepauze == 1) { 3348 | balancecells = 0; 3349 | } 3350 | 3351 | 3352 | msg.id = 0x080 | (nextmes); 3353 | msg.len = 8; 3354 | if (balancecells == 1) { 3355 | msg.buf[0] = lowByte((uint16_t((bms.getLowCellVolt()) * 1000) + 5)); 3356 | msg.buf[1] = highByte((uint16_t((bms.getLowCellVolt()) * 1000) + 5)); 3357 | } else { 3358 | msg.buf[0] = 0xC7; 3359 | msg.buf[1] = 0x10; 3360 | } 3361 | msg.buf[2] = 0x00; //balancing bits 3362 | msg.buf[3] = 0x00; //balancing bits 3363 | 3364 | if (testcycle < 3) { 3365 | msg.buf[4] = 0x20; 3366 | msg.buf[5] = 0x00; 3367 | } else { 3368 | 3369 | if (balancecells == 1) { 3370 | msg.buf[4] = 0x48; 3371 | } else { 3372 | msg.buf[4] = 0x40; 3373 | } 3374 | msg.buf[5] = 0x01; 3375 | } 3376 | 3377 | msg.buf[6] = mescycle << 4; 3378 | if (testcycle == 2) { 3379 | msg.buf[6] = msg.buf[6] + 0x04; 3380 | } 3381 | 3382 | msg.buf[7] = getcheck(msg, nextmes); 3383 | 3384 | delay(2); 3385 | Can0.write(msg); 3386 | nextmes++; 3387 | 3388 | if (bms.checkstatus() == true) { 3389 | resetbalancedebug(); 3390 | } 3391 | } 3392 | 3393 | void resetwdog() { 3394 | noInterrupts(); // No - reset WDT 3395 | WDOG_REFRESH = 0xA602; 3396 | WDOG_REFRESH = 0xB480; 3397 | interrupts(); 3398 | } 3399 | 3400 | void pwmcomms() { 3401 | int p = 0; 3402 | p = map((currentact * 0.001), pwmcurmin, pwmcurmax, 50, 255); 3403 | analogWrite(OUT7, p); 3404 | /* 3405 | Serial.println(); 3406 | Serial.print(p*100/255); 3407 | Serial.print(" OUT8 "); 3408 | */ 3409 | if (bms.getLowCellVolt() < settings.UnderVSetpoint) { 3410 | analogWrite(OUT8, 224); //12V to 10V converter 1.5V 3411 | } else { 3412 | p = map(SOC, 0, 100, 220, 50); 3413 | analogWrite(OUT8, p); //2V to 10V converter 1.5-10V 3414 | } 3415 | /* 3416 | Serial.println(); 3417 | Serial.print(p*100/255); 3418 | Serial.print(" OUT7 "); 3419 | */ 3420 | } 3421 | 3422 | void dashupdate() { 3423 | Serial2.write("stat.txt="); 3424 | Serial2.write(0x22); 3425 | if (settings.ESSmode == 1) { 3426 | switch (bmsstatus) { 3427 | case (Boot): 3428 | Serial2.print(" Active "); 3429 | break; 3430 | case (Error): 3431 | Serial2.print(" Error "); 3432 | break; 3433 | } 3434 | } else { 3435 | switch (bmsstatus) { 3436 | case (Boot): 3437 | Serial2.print(" Boot "); 3438 | break; 3439 | 3440 | case (Ready): 3441 | Serial2.print(" Ready "); 3442 | break; 3443 | 3444 | case (Precharge): 3445 | Serial2.print(" Precharge "); 3446 | break; 3447 | 3448 | case (Drive): 3449 | Serial2.print(" Drive "); 3450 | break; 3451 | 3452 | case (Charge): 3453 | Serial2.print(" Charge "); 3454 | break; 3455 | 3456 | case (Error): 3457 | Serial2.print(" Error "); 3458 | break; 3459 | } 3460 | } 3461 | Serial2.write(0x22); 3462 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3463 | Serial2.write(0xff); 3464 | Serial2.write(0xff); 3465 | Serial2.print("soc.val="); 3466 | Serial2.print(SOC); 3467 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3468 | Serial2.write(0xff); 3469 | Serial2.write(0xff); 3470 | Serial2.print("soc1.val="); 3471 | Serial2.print(SOC); 3472 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3473 | Serial2.write(0xff); 3474 | Serial2.write(0xff); 3475 | Serial2.print("current.val="); 3476 | Serial2.print(currentact / 100, 0); 3477 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3478 | Serial2.write(0xff); 3479 | Serial2.write(0xff); 3480 | Serial2.print("temp.val="); 3481 | Serial2.print(bms.getAvgTemperature(), 0); 3482 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3483 | Serial2.write(0xff); 3484 | Serial2.write(0xff); 3485 | Serial2.print("templow.val="); 3486 | Serial2.print(bms.getLowTemperature(), 0); 3487 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3488 | Serial2.write(0xff); 3489 | Serial2.write(0xff); 3490 | Serial2.print("temphigh.val="); 3491 | Serial2.print(bms.getHighTemperature(), 0); 3492 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3493 | Serial2.write(0xff); 3494 | Serial2.write(0xff); 3495 | Serial2.print("volt.val="); 3496 | Serial2.print(bms.getPackVoltage() * 10, 0); 3497 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3498 | Serial2.write(0xff); 3499 | Serial2.write(0xff); 3500 | Serial2.print("lowcell.val="); 3501 | Serial2.print(bms.getLowCellVolt() * 1000, 0); 3502 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3503 | Serial2.write(0xff); 3504 | Serial2.write(0xff); 3505 | Serial2.print("highcell.val="); 3506 | Serial2.print(bms.getHighCellVolt() * 1000, 0); 3507 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3508 | Serial2.write(0xff); 3509 | Serial2.write(0xff); 3510 | Serial2.print("firm.val="); 3511 | Serial2.print(firmver); 3512 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3513 | Serial2.write(0xff); 3514 | Serial2.write(0xff); 3515 | Serial2.print("celldelta.val="); 3516 | Serial2.print((bms.getHighCellVolt() - bms.getLowCellVolt()) * 1000, 0); 3517 | Serial2.write(0xff); // We always have to send this three lines after each command sent to the nextion display. 3518 | Serial2.write(0xff); 3519 | Serial2.write(0xff); 3520 | Serial2.write(0xff); 3521 | } 3522 | 3523 | 3524 | void chargercomms() { 3525 | if (settings.chargertype == Elcon) { 3526 | msg.id = 0x1806E5F4; //broadcast to all Elteks 3527 | msg.len = 8; 3528 | msg.ext = 1; 3529 | msg.buf[0] = highByte(uint16_t(settings.ChargeVsetpoint * settings.Scells * 10)); 3530 | msg.buf[1] = lowByte(uint16_t(settings.ChargeVsetpoint * settings.Scells * 10)); 3531 | msg.buf[2] = highByte(chargecurrent / ncharger); 3532 | msg.buf[3] = lowByte(chargecurrent / ncharger); 3533 | msg.buf[4] = 0x00; 3534 | msg.buf[5] = 0x00; 3535 | msg.buf[6] = 0x00; 3536 | msg.buf[7] = 0x00; 3537 | 3538 | Can0.write(msg); 3539 | msg.ext = 0; 3540 | } 3541 | 3542 | if (settings.chargertype == Eltek) { 3543 | msg.id = 0x2FF; //broadcast to all Elteks 3544 | msg.len = 7; 3545 | msg.buf[0] = 0x01; 3546 | msg.buf[1] = lowByte(1000); 3547 | msg.buf[2] = highByte(1000); 3548 | msg.buf[3] = lowByte(uint16_t(settings.ChargeVsetpoint * settings.Scells * 10)); 3549 | msg.buf[4] = highByte(uint16_t(settings.ChargeVsetpoint * settings.Scells * 10)); 3550 | msg.buf[5] = lowByte(chargecurrent / ncharger); 3551 | msg.buf[6] = highByte(chargecurrent / ncharger); 3552 | 3553 | Can0.write(msg); 3554 | } 3555 | if (settings.chargertype == BrusaNLG5) { 3556 | msg.id = chargerid1; 3557 | msg.len = 7; 3558 | msg.buf[0] = 0x80; 3559 | /* 3560 | if (chargertoggle == 0) 3561 | { 3562 | msg.buf[0] = 0x80; 3563 | chargertoggle++; 3564 | } 3565 | else 3566 | { 3567 | msg.buf[0] = 0xC0; 3568 | chargertoggle = 0; 3569 | } 3570 | */ 3571 | if (digitalRead(IN2) == LOW) //Gen OFF 3572 | { 3573 | msg.buf[1] = highByte(maxac1 * 10); 3574 | msg.buf[2] = lowByte(maxac1 * 10); 3575 | } else { 3576 | msg.buf[1] = highByte(maxac2 * 10); 3577 | msg.buf[2] = lowByte(maxac2 * 10); 3578 | } 3579 | msg.buf[5] = highByte(chargecurrent / ncharger); 3580 | msg.buf[6] = lowByte(chargecurrent / ncharger); 3581 | msg.buf[3] = highByte(uint16_t(((settings.ChargeVsetpoint * settings.Scells) - chargerendbulk) * 10)); 3582 | msg.buf[4] = lowByte(uint16_t(((settings.ChargeVsetpoint * settings.Scells) - chargerendbulk) * 10)); 3583 | Can0.write(msg); 3584 | 3585 | delay(2); 3586 | 3587 | msg.id = chargerid2; 3588 | msg.len = 7; 3589 | msg.buf[0] = 0x80; 3590 | if (digitalRead(IN2) == LOW) //Gen OFF 3591 | { 3592 | msg.buf[1] = highByte(maxac1 * 10); 3593 | msg.buf[2] = lowByte(maxac1 * 10); 3594 | } else { 3595 | msg.buf[1] = highByte(maxac2 * 10); 3596 | msg.buf[2] = lowByte(maxac2 * 10); 3597 | } 3598 | msg.buf[3] = highByte(uint16_t(((settings.ChargeVsetpoint * settings.Scells) - chargerend) * 10)); 3599 | msg.buf[4] = lowByte(uint16_t(((settings.ChargeVsetpoint * settings.Scells) - chargerend) * 10)); 3600 | msg.buf[5] = highByte(chargecurrent / ncharger); 3601 | msg.buf[6] = lowByte(chargecurrent / ncharger); 3602 | Can0.write(msg); 3603 | } 3604 | if (settings.chargertype == ChevyVolt) { 3605 | msg.id = 0x30E; 3606 | msg.len = 1; 3607 | msg.buf[0] = 0x02; //only HV charging , 0x03 hv and 12V charging 3608 | Can0.write(msg); 3609 | 3610 | msg.id = 0x304; 3611 | msg.len = 4; 3612 | msg.buf[0] = 0x40; //fixed 3613 | if ((chargecurrent * 2) > 255) { 3614 | msg.buf[1] = 255; 3615 | } else { 3616 | msg.buf[1] = (chargecurrent * 2); 3617 | } 3618 | if ((settings.ChargeVsetpoint * settings.Scells) > 200) { 3619 | msg.buf[2] = highByte(uint16_t((settings.ChargeVsetpoint * settings.Scells) * 2)); 3620 | msg.buf[3] = lowByte(uint16_t((settings.ChargeVsetpoint * settings.Scells) * 2)); 3621 | } else { 3622 | msg.buf[2] = highByte(400); 3623 | msg.buf[3] = lowByte(400); 3624 | } 3625 | Can0.write(msg); 3626 | } 3627 | } 3628 | 3629 | uint8_t getcheck(CAN_message_t &msg, int id) { 3630 | unsigned char canmes[11]; 3631 | int meslen = msg.len + 1; //remove one for crc and add two for id bytes 3632 | canmes[1] = msg.id; 3633 | canmes[0] = msg.id >> 8; 3634 | 3635 | for (int i = 0; i < (msg.len - 1); i++) { 3636 | canmes[i + 2] = msg.buf[i]; 3637 | } 3638 | /* 3639 | Serial.println(); 3640 | for (int i = 0; i < meslen; i++) 3641 | { 3642 | Serial.print(canmes[i], HEX); 3643 | Serial.print("|"); 3644 | } 3645 | */ 3646 | return (crc8.get_crc8(canmes, meslen, finalxor[id])); 3647 | } 3648 | 3649 | void resetbalancedebug() { 3650 | msg.id = 0x0B0; //broadcast to all Elteks 3651 | msg.len = 8; 3652 | msg.ext = 0; 3653 | msg.buf[0] = 0xFF; 3654 | msg.buf[1] = 0x00; 3655 | msg.buf[2] = 0xCD; 3656 | msg.buf[3] = 0xA2; 3657 | msg.buf[4] = 0x00; 3658 | msg.buf[5] = 0x00; 3659 | msg.buf[6] = 0x00; 3660 | msg.buf[7] = 0x00; 3661 | 3662 | Can0.write(msg); 3663 | } 3664 | 3665 | void resetIDdebug() { 3666 | //Rest all possible Ids 3667 | for (int ID = 0; ID < 15; ID++) { 3668 | msg.id = 0x0A0; //broadcast to all CSC 3669 | msg.len = 8; 3670 | msg.ext = 0; 3671 | msg.buf[0] = 0xA1; 3672 | msg.buf[1] = ID; 3673 | msg.buf[2] = 0xFF; 3674 | msg.buf[3] = 0xFF; 3675 | msg.buf[4] = 0xFF; 3676 | msg.buf[5] = 0xFF; 3677 | msg.buf[6] = 0xFF; 3678 | msg.buf[7] = 0xFF; 3679 | 3680 | Can0.write(msg); 3681 | 3682 | delay(2); 3683 | } 3684 | //NextID = 0; 3685 | 3686 | //check for found unassigned CSC 3687 | Unassigned = 0; 3688 | 3689 | msg.id = 0x0A0; //broadcast to all CSC 3690 | msg.len = 8; 3691 | msg.ext = 0; 3692 | msg.buf[0] = 0x37; 3693 | msg.buf[1] = 0xFF; 3694 | msg.buf[2] = 0xFF; 3695 | msg.buf[3] = 0xFF; 3696 | msg.buf[4] = 0xFF; 3697 | msg.buf[5] = 0xFF; 3698 | msg.buf[6] = 0xFF; 3699 | msg.buf[7] = 0xFF; 3700 | 3701 | Can0.write(msg); 3702 | } 3703 | 3704 | void findUnassigned() { 3705 | Unassigned = 0; 3706 | //check for found unassigned CSC 3707 | msg.id = 0x0A0; //broadcast to all CSC 3708 | msg.len = 8; 3709 | msg.ext = 0; 3710 | msg.buf[0] = 0x37; 3711 | msg.buf[1] = 0xFF; 3712 | msg.buf[2] = 0xFF; 3713 | msg.buf[3] = 0xFF; 3714 | msg.buf[4] = 0xFF; 3715 | msg.buf[5] = 0xFF; 3716 | msg.buf[6] = 0xFF; 3717 | msg.buf[7] = 0xFF; 3718 | 3719 | Can0.write(msg); 3720 | } 3721 | 3722 | void assignID() { 3723 | msg.id = 0x0A0; //broadcast to all CSC 3724 | msg.len = 8; 3725 | msg.ext = 0; 3726 | msg.buf[0] = 0x12; 3727 | msg.buf[1] = 0xAB; 3728 | msg.buf[2] = DMC[0]; 3729 | msg.buf[3] = DMC[1]; 3730 | msg.buf[4] = DMC[2]; 3731 | msg.buf[5] = DMC[3]; 3732 | msg.buf[6] = 0xFF; 3733 | msg.buf[7] = 0xFF; 3734 | 3735 | Can0.write(msg); 3736 | 3737 | delay(30); 3738 | 3739 | msg.buf[1] = 0xBA; 3740 | msg.buf[2] = DMC[4]; 3741 | msg.buf[3] = DMC[5]; 3742 | msg.buf[4] = DMC[6]; 3743 | msg.buf[5] = DMC[7]; 3744 | 3745 | Can0.write(msg); 3746 | 3747 | delay(10); 3748 | msg.buf[0] = 0x5B; 3749 | msg.buf[1] = NextID; 3750 | Can0.write(msg); 3751 | 3752 | delay(10); 3753 | msg.buf[0] = 0x37; 3754 | msg.buf[1] = NextID; 3755 | Can0.write(msg); 3756 | 3757 | NextID++; 3758 | 3759 | findUnassigned(); 3760 | } 3761 | 3762 | void isrCP() { 3763 | if (digitalRead(IN4) == LOW) { 3764 | duration = micros() - pilottimer; 3765 | pilottimer = micros(); 3766 | } else { 3767 | accurlim = ((duration - (micros() - pilottimer + 35)) * 60) / duration; //pilottimer + "xx" optocoupler decade ms 3768 | } 3769 | } // ******** end of isr CP ******** 3770 | 3771 | void low_voltage_isr(void) { 3772 | EEPROM.update(1000, uint8_t(SOC)); 3773 | 3774 | PMC_LVDSC2 |= PMC_LVDSC2_LVWACK; // clear if we can 3775 | PMC_LVDSC1 |= PMC_LVDSC1_LVDACK; 3776 | } 3777 | --------------------------------------------------------------------------------