Can bus battery monitor with OLED

From Public Wiki
Revision as of 07:03, 1 November 2020 by Legg (talk | contribs) (→‎Code)
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 }