Can bus battery monitor with OLED
Jump to navigation
Jump to search
Synopsis
This code is designed for the Arduino UNO. Attached is a SSD1306 compatible OLED module and a MCP2515 CAN module. This is designed to monitor cell voltages in large battery packs containing LiFePO4 cells in arrays. In this particular instance, there are 5 packs, each containing 8 cells. The output is delivered on 1 second intervals into a spreadsheet friendly format.
Notes
The OLED screen is disabled due to unexplained interactions with the Arduino IDE as explained in comments on lines 73-74 and 282.
Code
1 //#define EN_SCREEN
2 //#define EN_ARRAY
3
4 //Arduino UNO
5 //MCP2515
6 ////Pin 13 SPI clock
7 ////Pin 12 MISO
8 ////Pin 11 MOSI
9 ////Pin 10 Cable Select
10 //SSD1306
11 ////Pin A4 SDA
12 ////Pin A5 SCL
13 //ALARM
14 ////Pin 08 Enable
15
16 #include <SPI.h>
17 #include "mcp_can.h"
18
19 //Graphics
20 #include <Wire.h>
21 #include <Adafruit_GFX.h>
22 #include <Adafruit_SSD1306.h>
23
24 #define SCREEN_WIDTH 128 // OLED display width, in pixels
25 #define SCREEN_HEIGHT 64 // OLED display height, in pixels
26 // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
27 #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
28 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
29
30 //Software constants
31 #define NUMPACKS 5 //Number of packs installed on line. Must edit knownID[] array
32 #define NUMCELLS 8 //Hazardous to modify
33 #define REFRESH 1000 //Refresh rate display
34 #define ALARM 3.645 //Alarm threshold
35
36
37 bool buzzer = HIGH;
38 const int spiCSPin = 10;
39 boolean ledON = 1;
40 float voltage[NUMPACKS][NUMCELLS];
41 float sum[NUMPACKS];
42 long knownID[NUMPACKS] = {0x00000004, 0x00000006, 0x00000020, 0x00000021, 0x00000022};
43 long packID;
44 int slot;
45
46 float Pack = 0.0;
47
48 unsigned char bufA[10];
49 unsigned char buf0[10];
50 unsigned char buf3[10];
51 unsigned char buf5[10];
52 unsigned char buf20[10];
53 unsigned char buf21[10];
54 unsigned char buf22[10];
55
56 unsigned long time;
57 unsigned long TriggerTime = 0;
58 unsigned long unknownId = 0;
59
60 MCP_CAN CAN(spiCSPin);
61
62 void setup()
63 {
64 Serial.begin(115200);
65
66 //Graphics
67
68 #ifdef EN_SCREEN
69 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
70 { // Address 0x3D for 128x64
71 Serial.println(F("SSD1306 allocation failed"));
72 for(;;); // Don't proceed, loop forever
73 }
74 testdrawchar();
75 #endif
76
77 //Pin 8 voltage alarm
78 pinMode(8, OUTPUT);
79 //Initialize against undefined values
80 for(int i=0;i<NUMPACKS;i++)
81 {
82 for(int j=0;j<NUMCELLS;j++)
83 {
84 voltage[i][j]=0.0;
85 }
86 }
87
88
89
90 while (CAN_OK != CAN.begin(CAN_125KBPS,MCP_8MHz))
91 {
92 Serial.println("CAN BUS Init Failed, waiting for hardware...");
93 delay(100);
94 }
95 Serial.println("CAN BUS Init OK!");
96
97 }
98
99 void loop()
100 {
101
102 long msgID = 0x00000FFF;
103 bool skip = LOW;
104 unsigned char len = 0;
105 unsigned char buf[10];
106 unsigned long canId = 0x00000000;
107
108 if(CAN_MSGAVAIL == CAN.checkReceive())
109 {
110
111 CAN.readMsgBuf(&len, buf);
112 canId = CAN.getCanId();
113 if (canId & 0xFFFFF000 != 0x08010000)
114 {
115 Serial.println("Unsupported device attached! Correct and restart monitor.");
116 Serial.print("Device: 0x");
117 Serial.print(canId & 0xFFFFF000,HEX);
118 Serial.println(".");
119 //Sound alarm
120 digitalWrite(8,1);
121 while(1){}
122 }
123 //detect if pack is registered
124 packID = canId & 0x000000FF;
125 slot = 0xFF;
126 for(int i = 0; i<NUMPACKS ; i++)
127 {
128 if(knownID[i]==packID)
129 {
130 slot=i;
131 }
132 if(i==NUMPACKS-1 && slot==0xFF)
133 {
134 Serial.println("Unregistered Device attached! Skipping!");
135 Serial.print(" Device : 0x");
136 Serial.print(packID,HEX);
137 Serial.println(".");
138 skip = HIGH;
139 }
140 }
141 //If pack is registered, collect it's data into local storage.
142 if(!skip)
143 {
144
145 msgID = canId & 0x00000F00;
146 //Interpret message 3
147 if (msgID == 0x00000300)
148 {
149 for(int i = 0; i<len; i++)
150 {
151 buf3[i] = buf[i];
152 }
153 }
154 //Interpret message 3
155 else if (msgID == 0x00000500)
156 {
157 for(int i = 0; i<len; i++)
158 {
159 buf5[i] = buf[i];
160 }
161 }
162 //Interpret message A
163 else if (msgID == 0x00000A00)
164 {
165 for(int i = 0; i<len; i++)
166 {
167 bufA[i] = buf[i];
168 }
169 }
170 //Interpret message 0
171 else if (msgID == 0x00000000)
172 {
173 for(int i = 0; i<len; i++)
174 {
175 buf0[i] = buf[i];
176 }
177 }
178 //Interpret message 2
179 else if (msgID == 0x00000200)
180 {
181 if (buf[0] == 0)
182 {
183 unsigned int Cell = buf[2] * 256 + buf[1];
184 voltage[slot][7] = Cell / 1000.0;
185 Cell = buf[4] * 256 + buf[3];
186 voltage[slot][6] = Cell / 1000.0;
187 Cell = buf[6] * 256 + buf[5];
188 voltage[slot][5] = Cell / 1000.0;
189
190 for(int i = 0; i<len; i++)
191 {
192 buf20[i] = buf[i];
193 }
194 }
195 if (buf[0] == 1)
196 {
197 unsigned int Cell = buf[2] * 256 + buf[1];
198 voltage[slot][4] = Cell / 1000.0;
199 Cell = buf[4] * 256 + buf[3];
200 voltage[slot][3] = Cell / 1000.0;
201 Cell = buf[6] * 256 + buf[5];
202 voltage[slot][2] = Cell / 1000.0;
203
204 for(int i = 0; i<len; i++)
205 {
206 buf21[i] = buf[i];
207 }
208 }
209 if (buf[0] == 2)
210 {
211 unsigned int Cell = buf[2] * 256 + buf[1];
212 voltage[slot][1] = Cell / 1000.0;
213 Cell = buf[4] * 256 + buf[3];
214 voltage[slot][0] = Cell / 1000.0;
215
216 for(int i = 0; i<len; i++)
217 {
218 buf22[i] = buf[i];
219 }
220 }
221 }
222 else
223 {
224 unknownId = canId;
225 Serial.println("Unexpected Message type detected!");
226 Serial.println(canId & 0x00000F00, HEX);
227 }
228
229 //Run every REFRESHms
230 time = millis();
231 if (time >= TriggerTime)
232 {
233
234 //Detect for alarm condition
235 for(int i=0; i<NUMPACKS;i++)
236 {
237 for(int j=0;j<NUMCELLS;j++)
238 {
239 #ifdef EN_ARRAY
240 if(voltage[i][j]>=ALARM) //KILLER LINE
241 buzzer=HIGH;
242 else
243 buzzer=LOW;
244 #endif
245 }
246 }
247
248 //Execute alarm condition
249 if(buzzer)
250 digitalWrite(8,1);
251 else
252 digitalWrite(8,0);
253 TriggerTime = TriggerTime + REFRESH;
254 Pack=0.0;
255
256 //Dump cell voltages to CSV
257 for(int i=0; i<NUMPACKS;i++)
258 {
259 Pack=0.0;
260 for(int j=0; j<NUMCELLS ; j++)
261 {
262 #ifdef EN_ARRAY
263 Serial.print(voltage[i][j],3); //KILLER
264 #endif
265 Serial.print(",");
266 #ifdef EN_ARRAY
267 Pack += voltage[i][j]; //KILLER
268 #endif
269 }
270 Serial.print(Pack,3);
271 if(NUMPACKS-1!=i)
272 Serial.print(',');
273 else
274 Serial.println();
275 }
276
277 if(unknownId != 0)
278 {
279 Serial.print("Unknown Message: ");
280 Serial.print(unknownId, HEX);
281 Serial.println();
282 }
283
284 }
285
286 }
287 else skip=LOW;
288 }
289
290 }
291
292 void testdrawchar(void)
293 {
294 display.clearDisplay();
295 display.setTextSize(1); // Normal 1:1 pixel scale
296 display.setTextColor(SSD1306_WHITE); // Draw white text
297 display.setCursor(0, 0); // Start at top-left corner
298 display.cp437(true); // Use full 256 char 'Code Page 437' font
299 Serial.println("Testing...");
300 display.setCursor(0,0); // Start at top-left corner
301 display.println(F(" * Testing *"));
302 display.display();
303 display.clearDisplay();
304 delay(500);
305 display.setCursor(28,25); // Start at top-left corner
306 display.println(F(" * Testing *"));
307 display.display();
308 delay(500);
309 display.clearDisplay();
310 }