Can bus battery monitor with OLED

From Public Wiki
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

There is unexplained behavior in this sketch. Two DEFINE clauses were created EN_SCREEN (enable screen) and EN_ARRAY (enable array).

If EN_SCREEN is defined, and EN_ARRAY is undefined : The screen demo in setup() performs normally

If EN_SCREEN is undefined, and EN_ARRAY is defined : The CAN bus demo in loop() performs normally

If EN_SCREEN is defined, and EN_ARRAY is defined : An initialization error exists with the display.begin() function in in the setup() section, even though the added lines that are in the loop() section and not yet executed. The existence of these array functions in the code segment cause the display.begin() to fail.

The EN_SCREEN code is within lines 68-75.

The EN_ARRAY code is within lines 240-243, 263, 265 and 267.

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 }