Can bus battery monitor with OLED

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