-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSbsCAN.cpp
More file actions
207 lines (199 loc) · 9.55 KB
/
SbsCAN.cpp
File metadata and controls
207 lines (199 loc) · 9.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// This was tested with:
// Arduino 101 on pins 0/1 (Serial1) for the serial connection to the CAN module
// Arduino Due on pins 19/18 (Serial1) for the serial connection to the CAN module
// Longan Serial CAN Bus 1.0 (MCP2551 and MCP2515 chips) https://docs.longan-labs.cc/can_bus/
// Wiring as documented in "2018-07-20 Report Leader 50V Battery CAN Readings": https://docs.google.com/document/d/1OmkW_UTGWCwyDriThNI7dI2qDYE3IVobrKOOHKtsnHs/edit#
// The hardware-serial branch of the CAN serial library https://github.com/fhackenberger/Serial_CAN_Arduino revision https://github.com/fhackenberger/Serial_CAN_Arduino/commit/2376ae2617c075af54695e48a02da84138ba8f14
//
// ATTENTION: During testing, after a re-flash, I have to completely powerdown the Arduino and the battery (two long presses) before powering it up again.
#include <limits.h>
#include "SbsCAN.h"
/** According to Spec: Smart Battery Solutions "Specification CAN Bus (General) Project: 10bis14s BMS", 29.07.2015 */
unsigned long BMS_INFO_IDS[] = {
0x171, 0x172, 0x173, 0x174, 0x175, 0x176 // CAN message IDs for the various informational messages the battery sends
};
unsigned long BMS_CTRL_ID = 0x160; // CAN message for the control message to switch switch the battery state
// Bitmasks for various bits within the CAN messages
char MASK_BMS_ERR_Temp_Powerstage_1 = 0b00000001;
char MASK_BMS_ERR_Temp_Powerstage_2 = 0b00000010;
char MASK_BMS_ERR_Charge_Current = 0b00000100;
char MASK_BMS_ERR_Discharge_Current = 0b00001000;
char MASK_BMS_ERR_Pack_Voltage_Max = 0b00010000;
char MASK_BMS_ERR_Analog_Overvoltage = 0b00100000;
char MASK_BMS_ERR_Curr_Sensor_Offset = 0b01000000;
char MASK_BMS_ERR_EEPROM = 0b10000000;
char MASK_BMS_ERR_CM_CRC = 0b00000001;
char MASK_BMS_ERR_External_Enable = 0b00000010;
char MASK_BMS_ERR_CM_Alert = 0b00000100;
char MASK_BMS_ERR_CM_Fault = 0b00001000;
char MASK_BMS_ERR_Powerstage = 0b00010000;
char MASK_BMS_ERR_PreCharge = 0b00100000;
char MASK_BMS_ERR_Output_Voltage_High = 0b01000000;
char MASK_BMS_ERR_Pack_Voltage_Min = 0b10000000;
char MASK_BMS_ERR_Discharge_Voltage = 0b00000001;
char MASK_BMS_ERR_CM_Cell_Undervoltage = 0b00000010;
char MASK_BMS_ERR_CM_Cell_Overvoltage = 0b00000100;
char MASK_BMS_ERR_Analog_Overcurrent = 0b00001000;
char MASK_BMS_ERR_Overtemp_Charge = 0b00010000;
char MASK_BMS_ERR_Overtemp_Discharge = 0b00100000;
char MASK_BMS_ERR_Undertemp_Charge = 0b01000000;
char MASK_BMS_ERR_Undertemp_Discharge = 0b10000000;
char MASK_BMS_ERR_Curr_Flow_Passive_State = 0b00000001;
char MASK_BMS_ERR_CAN_Timeout = 0b00000010;
char MASK_BMS_Charge_Plug_Detection = 0b00000100;
unsigned int bytesToUInt16(unsigned char* bytes, int startIdx) {
unsigned int val = 0;
val = bytes[startIdx] + (bytes[startIdx+1] << 8);
return val;
}
unsigned int bytesToInt16(unsigned char* bytes, int startIdx) {
int val = 0;
val = bytes[startIdx] | (bytes[startIdx+1] << 8);
return val;
}
// Used for debugging, put it into the decodeMsg method if required
/*
void printMsg(unsigned long id, unsigned char data[8]) {
Serial.print("DATA FROM ID: ");
Serial.println(id, HEX);
for(int i=0; i<8; i++) {
Serial.print("0x");
Serial.print(data[i], HEX);
Serial.print('\t');
}
Serial.println();
}*/
int BMSState::decodeMsg(unsigned long id, unsigned char* data, unsigned int dataLen) {
if(id == BMS_INFO_IDS[0]) { // BMS_Info_01 message
// TODO Validate status bits, BMS_Charge_Plug_Detection might help to test the code
this->packVoltage = bytesToUInt16(data, 0) * 0.0078125; // Factor according to battery CAN spec page 6
this->packCurrent = bytesToInt16(data, 2) * 0.03125; // Factor according to battery CAN spec page 6
this->errField = data[4] << (26 - 8) + (data[5] << (26 - 2*8)) + (data[6] << (26 - 3*8)) + (data[7] & 0b00000011);
this->errTempPowerstage1 = data[4] & MASK_BMS_ERR_Temp_Powerstage_1 ? true : false;
this->errTempPowerstage2 = data[4] & MASK_BMS_ERR_Temp_Powerstage_2 ? true : false;
this->errChargeCurrent = data[4] & MASK_BMS_ERR_Charge_Current ? true : false;
this->errDischargeCurrent = data[4] & MASK_BMS_ERR_Discharge_Current ? true : false;
this->errPackVoltMax = data[4] & MASK_BMS_ERR_Pack_Voltage_Max ? true : false;
this->errAnalogOverVolt = data[4] & MASK_BMS_ERR_Analog_Overvoltage ? true : false;
this->errCurrSensorOffset = data[4] & MASK_BMS_ERR_Curr_Sensor_Offset ? true : false;
this->errEeprom = data[4] & MASK_BMS_ERR_EEPROM ? true : false;
this->errCmCrc = data[5] & MASK_BMS_ERR_CM_CRC ? true : false;
this->errExternalEnable = data[5] & MASK_BMS_ERR_External_Enable ? true : false;
this->errCmAlert = data[5] & MASK_BMS_ERR_CM_Alert ? true : false;
this->errCmFault = data[5] & MASK_BMS_ERR_CM_Fault ? true : false;
this->errPowerstage = data[5] & MASK_BMS_ERR_Powerstage ? true : false;
this->errPreCharge = data[5] & MASK_BMS_ERR_PreCharge ? true : false;
this->errOutputVoltHigh = data[5] & MASK_BMS_ERR_Output_Voltage_High ? true : false;
this->errPackVoltMin = data[5] & MASK_BMS_ERR_Pack_Voltage_Min ? true : false;
this->errDischargeVolt = data[6] & MASK_BMS_ERR_Discharge_Voltage ? true : false;
this->errCmCellUntervolt = data[6] & MASK_BMS_ERR_CM_Cell_Undervoltage ? true : false;
this->errCmCellOvervolt = data[6] & MASK_BMS_ERR_CM_Cell_Overvoltage ? true : false;
this->errAnalogOvercurrent = data[6] & MASK_BMS_ERR_Analog_Overcurrent ? true : false;
this->errOvertempCharge = data[6] & MASK_BMS_ERR_Overtemp_Charge ? true : false;
this->errOvertempDischarge = data[6] & MASK_BMS_ERR_Overtemp_Discharge ? true : false;
this->errUndertempCharge = data[6] & MASK_BMS_ERR_Undertemp_Charge ? true : false;
this->errUnsertempDischarge = data[6] & MASK_BMS_ERR_Undertemp_Discharge ? true : false;
this->errCurrFlowPassiveState = data[7] & MASK_BMS_ERR_Curr_Flow_Passive_State ? true : false;
this->errCanTimeout = data[7] & MASK_BMS_ERR_CAN_Timeout ? true : false;
this->errChargePlugDetection = data[7] & MASK_BMS_Charge_Plug_Detection ? true : false;
this->msgCountInfo01++;
return 1;
}else if(id == BMS_INFO_IDS[1]) { // BMS_Info_02 message
//printMsg(id, data);
this->bmsState = bytesToUInt16(data, 0);
this->stateOfCharge = data[2];
this->stateOfHealth = 0.5 * (unsigned int)data[3]; // Factor according to battery CAN spec page 9
this->remainingCapacity_mAh = bytesToUInt16(data, 4);
this->lastFullCapacity_mAh = bytesToUInt16(data, 6);
this->msgCountInfo02++;
return 2;
}else if(id == BMS_INFO_IDS[2]) { // BMS_INFO_03 message
this->cellVolt01 = data[0];
this->cellVolt02 = data[1];
this->cellVolt03 = data[2];
this->cellVolt04 = data[3];
this->cellVolt05 = data[4];
this->cellVolt06 = data[5];
this->cellVolt07 = data[6];
this->cellVolt08 = data[7];
this->msgCountInfo03++;
return 3;
}else if(id == BMS_INFO_IDS[3]) { // BMS_INFO_04 message
this->cellVolt09 = data[0];
this->cellVolt10 = data[1];
this->cellVolt11 = data[2];
this->cellVolt12 = data[3];
this->cellVolt13 = data[4];
this->cellVolt14 = data[5];
this->msgCountInfo04++;
return 4;
}else if(id == BMS_INFO_IDS[4]) { // BMS_INFO_05 message
this->cellBalBits1 = data[0]; // Cells 1-6
this->cellBalBits2 = data[1]; // Cells 7-11
this->cellBalBits3 = data[2]; // Cells 12-14
this->msgCountInfo05++;
return 5;
}else if(id == BMS_INFO_IDS[5]) { // BMS_INFO_06 message
this->tempPowerstage1 = bytesToInt16(data, 0);
this->tempPowerstage2 = bytesToInt16(data, 2);
this->tempMCU = bytesToInt16(data, 4);
this->tempCell1 = data[6];
this->tempCell2 = data[7];
this->msgCountInfo06++;
return 6;
}else {
return -1;
}
return -1;
}
void BMSState::encodeSetStateMsg(unsigned int canState, unsigned long& id, unsigned char* msgData) {
// msgData = { 0 };
msgData[0] = (unsigned char)canState;
id = BMS_CTRL_ID;
return;
}
String BMSState::getBmsStateStr() {
switch(this->bmsState) {
case BMS_STATE_INIT1:
return String("INIT1");
case BMS_STATE_INIT2:
return String("INIT2");
case BMS_STATE_INIT3:
return String("INIT3");
case BMS_STATE_INIT4:
return String("INIT4");
case BMS_STATE_IDLE :
return String("IDLE ");
case BMS_STATE_DISCHARGE:
return String("DISCHARGE");
case BMS_STATE_CHARGE:
return String("CHARGE");
case BMS_STATE_FAULT:
return String("FAULT");
case BMS_STATE_CRIT_ERR:
return String("CRIT_ERR");
case BMS_STATE_PREPARE_DEEPSLEEP:
return String("PREPARE_DEEPSLEEP");
case BMS_STATE_DEEPSLEEP:
return String("DEEPSLEEP");
}
}
bool BMSState::isErrActive() {
return this->errField != 0;
}
unsigned int BMSState::bmsCtrlStateFromStr(String bmsCtrlStateStr) {
if(String("INACTIVE") == bmsCtrlStateStr)
return BMS_CTRL_STATE_INACTIVE;
else if(String("DISCHARGE") == bmsCtrlStateStr)
return BMS_CTRL_STATE_DISCHARGE;
else if(String("CHARGE") == bmsCtrlStateStr)
return BMS_CTRL_STATE_CHARGE;
else if(String("ALARM_MODE") == bmsCtrlStateStr)
return BMS_CTRL_STATE_ALARM_MODE;
else if(String("12V_MODE") == bmsCtrlStateStr)
return BMS_CTRL_STATE_12V_MODE;
else if(String("DEEP_SLEEP") == bmsCtrlStateStr)
return BMS_CTRL_STATE_DEEP_SLEEP;
return UINT_MAX;
}
// END FILE