Can bus battery monitor with OLED

From Public Wiki
Revision as of 07:00, 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)) //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 }