Difference between revisions of "Can bus battery monitor with OLED"
Jump to navigation
Jump to search
(→Code) |
|||
Line 5: | Line 5: | ||
==Code== | ==Code== | ||
<syntaxhighlight lang="C++" line='line'> | <syntaxhighlight lang="C++" line='line'> | ||
+ | //#define EN_SCREEN | ||
+ | #define EN_ARRAY | ||
+ | |||
//Arduino UNO | //Arduino UNO | ||
//MCP2515 | //MCP2515 | ||
Line 68: | Line 71: | ||
//Graphics | //Graphics | ||
− | + | ||
− | + | #ifdef EN_SCREEN | |
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) //Line 66 | if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) //Line 66 | ||
{ // Address 0x3D for 128x64 | { // Address 0x3D for 128x64 | ||
Serial.println(F("SSD1306 allocation failed")); | Serial.println(F("SSD1306 allocation failed")); | ||
for(;;); // Don't proceed, loop forever | for(;;); // Don't proceed, loop forever | ||
− | } | + | } |
− | |||
//If you comment line 74, then line 66 will fail | //If you comment line 74, then line 66 will fail | ||
testdrawchar(); //Line 74 | testdrawchar(); //Line 74 | ||
− | + | #endif | |
+ | |||
//Pin 8 voltage alarm | //Pin 8 voltage alarm | ||
pinMode(8, OUTPUT); | pinMode(8, OUTPUT); | ||
Line 99: | Line 102: | ||
} | } | ||
Serial.println("CAN BUS Init OK!"); | Serial.println("CAN BUS Init OK!"); | ||
+ | |||
} | } | ||
void loop() | void loop() | ||
{ | { | ||
+ | |||
long msgID = 0x00000FFF; | long msgID = 0x00000FFF; | ||
bool skip = LOW; | bool skip = LOW; | ||
Line 108: | Line 113: | ||
unsigned char buf[10]; | unsigned char buf[10]; | ||
unsigned long canId = 0x00000000; | unsigned long canId = 0x00000000; | ||
+ | |||
if(CAN_MSGAVAIL == CAN.checkReceive()) | if(CAN_MSGAVAIL == CAN.checkReceive()) | ||
{ | { | ||
+ | |||
CAN.readMsgBuf(&len, buf); | CAN.readMsgBuf(&len, buf); | ||
canId = CAN.getCanId(); | canId = CAN.getCanId(); | ||
Line 143: | Line 150: | ||
if(!skip) | if(!skip) | ||
{ | { | ||
+ | |||
msgID = canId & 0x00000F00; | msgID = canId & 0x00000F00; | ||
//Interpret message 3 | //Interpret message 3 | ||
Line 226: | Line 234: | ||
Serial.println(canId & 0x00000F00, HEX); | Serial.println(canId & 0x00000F00, HEX); | ||
} | } | ||
+ | |||
//Run every REFRESHms | //Run every REFRESHms | ||
time = millis(); | time = millis(); | ||
if (time >= TriggerTime) | if (time >= TriggerTime) | ||
{ | { | ||
+ | |||
//Detect for alarm condition | //Detect for alarm condition | ||
for(int i=0; i<NUMPACKS;i++) | for(int i=0; i<NUMPACKS;i++) | ||
Line 235: | Line 245: | ||
for(int j=0;j<NUMCELLS;j++) | for(int j=0;j<NUMCELLS;j++) | ||
{ | { | ||
− | if(voltage[i][j]>=ALARM) | + | #ifdef EN_ARRAY |
+ | if(voltage[i][j]>=ALARM) //KILLER LINE | ||
buzzer=HIGH; | buzzer=HIGH; | ||
else | else | ||
− | buzzer=LOW; | + | buzzer=LOW; |
+ | #endif | ||
} | } | ||
} | } | ||
+ | |||
//Execute alarm condition | //Execute alarm condition | ||
if(buzzer) | if(buzzer) | ||
digitalWrite(8,1); | digitalWrite(8,1); | ||
else | else | ||
− | digitalWrite(8,0); | + | digitalWrite(8,0); |
− | |||
TriggerTime = TriggerTime + REFRESH; | TriggerTime = TriggerTime + REFRESH; | ||
Pack=0.0; | Pack=0.0; | ||
+ | |||
//Dump cell voltages to CSV | //Dump cell voltages to CSV | ||
for(int i=0; i<NUMPACKS;i++) | for(int i=0; i<NUMPACKS;i++) | ||
{ | { | ||
Pack=0.0; | Pack=0.0; | ||
− | for(int j=0; j<NUMCELLS ; j++) | + | for(int j=0; j<NUMCELLS ; j++) |
{ | { | ||
− | Serial.print(voltage[i][j],3); | + | #ifdef EN_ARRAY |
+ | Serial.print(voltage[i][j],3); //KILLER | ||
+ | #endif | ||
Serial.print(","); | Serial.print(","); | ||
− | Pack += voltage[i][j]; | + | #ifdef EN_ARRAY |
+ | Pack += voltage[i][j]; //KILLER | ||
+ | #endif | ||
} | } | ||
Serial.print(Pack,3); | Serial.print(Pack,3); | ||
Line 263: | Line 280: | ||
Serial.print(','); | Serial.print(','); | ||
else | else | ||
− | Serial.println( | + | Serial.println(); |
} | } | ||
Line 272: | Line 289: | ||
Serial.println(); | Serial.println(); | ||
} | } | ||
+ | |||
} | } | ||
+ | |||
} | } | ||
else skip=LOW; | else skip=LOW; | ||
} | } | ||
+ | |||
} | } | ||
Line 285: | Line 305: | ||
display.setCursor(0, 0); // Start at top-left corner | display.setCursor(0, 0); // Start at top-left corner | ||
display.cp437(true); // Use full 256 char 'Code Page 437' font | display.cp437(true); // Use full 256 char 'Code Page 437' font | ||
− | + | Serial.println("Testing..."); | |
− | + | display.setCursor(0,0); // Start at top-left corner | |
− | + | display.println(F(" * Testing *")); | |
− | + | display.display(); | |
− | + | display.clearDisplay(); | |
− | + | delay(500); | |
− | + | display.setCursor(28,25); // Start at top-left corner | |
− | + | display.println(F(" * Testing *")); | |
− | + | display.display(); | |
− | + | delay(500); | |
− | + | display.clearDisplay(); | |
} | } | ||
</syntaxhighlight> | </syntaxhighlight> |
Revision as of 07:00, 1 November 2020
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)) //Line 66
70 { // Address 0x3D for 128x64
71 Serial.println(F("SSD1306 allocation failed"));
72 for(;;); // Don't proceed, loop forever
73 }
74
75 //If you comment line 74, then line 66 will fail
76 testdrawchar(); //Line 74
77 #endif
78
79 //Pin 8 voltage alarm
80 pinMode(8, OUTPUT);
81 //Initialize against undefined values
82 for(int i=0;i<NUMPACKS;i++)
83 {
84 for(int j=0;j<NUMCELLS;j++)
85 {
86 voltage[i][j]=0.0;
87 }
88 }
89
90
91
92 while (CAN_OK != CAN.begin(CAN_125KBPS,MCP_8MHz))
93 {
94 Serial.println("CAN BUS Init Failed, waiting for hardware...");
95 delay(100);
96 }
97 Serial.println("CAN BUS Init OK!");
98
99 }
100
101 void loop()
102 {
103
104 long msgID = 0x00000FFF;
105 bool skip = LOW;
106 unsigned char len = 0;
107 unsigned char buf[10];
108 unsigned long canId = 0x00000000;
109
110 if(CAN_MSGAVAIL == CAN.checkReceive())
111 {
112
113 CAN.readMsgBuf(&len, buf);
114 canId = CAN.getCanId();
115 if (canId & 0xFFFFF000 != 0x08010000)
116 {
117 Serial.println("Unsupported device attached! Correct and restart monitor.");
118 Serial.print("Device: 0x");
119 Serial.print(canId & 0xFFFFF000,HEX);
120 Serial.println(".");
121 //Sound alarm
122 digitalWrite(8,1);
123 while(1){}
124 }
125 //detect if pack is registered
126 packID = canId & 0x000000FF;
127 slot = 0xFF;
128 for(int i = 0; i<NUMPACKS ; i++)
129 {
130 if(knownID[i]==packID)
131 {
132 slot=i;
133 }
134 if(i==NUMPACKS-1 && slot==0xFF)
135 {
136 Serial.println("Unregistered Device attached! Skipping!");
137 Serial.print(" Device : 0x");
138 Serial.print(packID,HEX);
139 Serial.println(".");
140 skip = HIGH;
141 }
142 }
143 //If pack is registered, collect it's data into local storage.
144 if(!skip)
145 {
146
147 msgID = canId & 0x00000F00;
148 //Interpret message 3
149 if (msgID == 0x00000300)
150 {
151 for(int i = 0; i<len; i++)
152 {
153 buf3[i] = buf[i];
154 }
155 }
156 //Interpret message 3
157 else if (msgID == 0x00000500)
158 {
159 for(int i = 0; i<len; i++)
160 {
161 buf5[i] = buf[i];
162 }
163 }
164 //Interpret message A
165 else if (msgID == 0x00000A00)
166 {
167 for(int i = 0; i<len; i++)
168 {
169 bufA[i] = buf[i];
170 }
171 }
172 //Interpret message 0
173 else if (msgID == 0x00000000)
174 {
175 for(int i = 0; i<len; i++)
176 {
177 buf0[i] = buf[i];
178 }
179 }
180 //Interpret message 2
181 else if (msgID == 0x00000200)
182 {
183 if (buf[0] == 0)
184 {
185 unsigned int Cell = buf[2] * 256 + buf[1];
186 voltage[slot][7] = Cell / 1000.0;
187 Cell = buf[4] * 256 + buf[3];
188 voltage[slot][6] = Cell / 1000.0;
189 Cell = buf[6] * 256 + buf[5];
190 voltage[slot][5] = Cell / 1000.0;
191
192 for(int i = 0; i<len; i++)
193 {
194 buf20[i] = buf[i];
195 }
196 }
197 if (buf[0] == 1)
198 {
199 unsigned int Cell = buf[2] * 256 + buf[1];
200 voltage[slot][4] = Cell / 1000.0;
201 Cell = buf[4] * 256 + buf[3];
202 voltage[slot][3] = Cell / 1000.0;
203 Cell = buf[6] * 256 + buf[5];
204 voltage[slot][2] = Cell / 1000.0;
205
206 for(int i = 0; i<len; i++)
207 {
208 buf21[i] = buf[i];
209 }
210 }
211 if (buf[0] == 2)
212 {
213 unsigned int Cell = buf[2] * 256 + buf[1];
214 voltage[slot][1] = Cell / 1000.0;
215 Cell = buf[4] * 256 + buf[3];
216 voltage[slot][0] = Cell / 1000.0;
217
218 for(int i = 0; i<len; i++)
219 {
220 buf22[i] = buf[i];
221 }
222 }
223 }
224 else
225 {
226 unknownId = canId;
227 Serial.println("Unexpected Message type detected!");
228 Serial.println(canId & 0x00000F00, HEX);
229 }
230
231 //Run every REFRESHms
232 time = millis();
233 if (time >= TriggerTime)
234 {
235
236 //Detect for alarm condition
237 for(int i=0; i<NUMPACKS;i++)
238 {
239 for(int j=0;j<NUMCELLS;j++)
240 {
241 #ifdef EN_ARRAY
242 if(voltage[i][j]>=ALARM) //KILLER LINE
243 buzzer=HIGH;
244 else
245 buzzer=LOW;
246 #endif
247 }
248 }
249
250 //Execute alarm condition
251 if(buzzer)
252 digitalWrite(8,1);
253 else
254 digitalWrite(8,0);
255 TriggerTime = TriggerTime + REFRESH;
256 Pack=0.0;
257
258 //Dump cell voltages to CSV
259 for(int i=0; i<NUMPACKS;i++)
260 {
261 Pack=0.0;
262 for(int j=0; j<NUMCELLS ; j++)
263 {
264 #ifdef EN_ARRAY
265 Serial.print(voltage[i][j],3); //KILLER
266 #endif
267 Serial.print(",");
268 #ifdef EN_ARRAY
269 Pack += voltage[i][j]; //KILLER
270 #endif
271 }
272 Serial.print(Pack,3);
273 if(NUMPACKS-1!=i)
274 Serial.print(',');
275 else
276 Serial.println();
277 }
278
279 if(unknownId != 0)
280 {
281 Serial.print("Unknown Message: ");
282 Serial.print(unknownId, HEX);
283 Serial.println();
284 }
285
286 }
287
288 }
289 else skip=LOW;
290 }
291
292 }
293
294 void testdrawchar(void)
295 {
296 display.clearDisplay();
297 display.setTextSize(1); // Normal 1:1 pixel scale
298 display.setTextColor(SSD1306_WHITE); // Draw white text
299 display.setCursor(0, 0); // Start at top-left corner
300 display.cp437(true); // Use full 256 char 'Code Page 437' font
301 Serial.println("Testing...");
302 display.setCursor(0,0); // Start at top-left corner
303 display.println(F(" * Testing *"));
304 display.display();
305 display.clearDisplay();
306 delay(500);
307 display.setCursor(28,25); // Start at top-left corner
308 display.println(F(" * Testing *"));
309 display.display();
310 delay(500);
311 display.clearDisplay();
312 }