CAN Battery Monitor with Epever Controller Integration + 3.5TFT Mega2560

From Public Wiki
Jump to navigation Jump to search

Synopsis

Notes

This was developed to test communication to the EPEVER AN Solar controller from the Arduino


Code

  1 //Arduino MEGA 2560
  2 //MCP2515
  3 ////Pin 52 SPI clock
  4 ////Pin 50 MISO
  5 ////Pin 51 MOSI
  6 ////Pin 53 Cable Select
  7 
  8 #include <SPI.h>
  9 #include <mcp_can.h>
 10 #include <LCDWIKI_GUI.h> //Core graphics library http://www.lcdwiki.com/3.5inch_Arduino_Display-UNO
 11 #include <LCDWIKI_KBV.h> //Hardware-specific library
 12 #include <TouchScreen.h> //touch library
 13 #include <ModbusMaster.h>
 14 
 15 //Software constants
 16 #define NUMPACKS                4     //Number of packs installed on line.  Must edit knownID[] array
 17 #define NUMCELLS                8     //Hazardous to modify
 18 #define REFRESH                 1000   //Refresh rate display
 19 #define ALARM                   3.640    //Alarm threshold
 20 #define LOWCELL                 3.100
 21 #define windDown_period         600000 //Delay before controller attempts to increase the charge voltage, giving cells time to settle
 22 #define windUp_period           60000
 23 #define windUpThreshold         3.550 //WindUp function will not call again until a cell reaches this voltage
 24 #define dischargeProtect_period 600000
 25 #define defaultPin              22 //Short pin 22 to ground to send default values in the next group to the controller
 26 
 27 //Define the default values for your pack here, value *100, no decimal
 28 #define def_BATT_TYPE           0000
 29 #define def_BATT_AH             880
 30 #define def_TEMP_COMP           300
 31 #define def_OVERVOLT_DISC_VAL   2950
 32 #define def_CHARGING_LIMIT_VAL  2860
 33 #define def_OVERVOLT_RECON_VAL  2880
 34 #define def_EQUILIBRIUM_CV_VAL  2830
 35 #define def_BOOST_CV_VAL        2830
 36 #define def_FLOAT_CV_VAL        2760        
 37 #define def_BOOST_RECON_VAL     2640 
 38 #define def_LOW_VOLT_RECON      2350
 39 #define def_UNDERVOLT_RECOV     2440
 40 #define def_UNDERVOLT_WARNING   2400
 41 #define def_LOW_VOLT_DISC       2220
 42 #define def_DISCHARGE_LIMIT     2120
 43 #define dayLightVolt            3500  
 44 
 45 //define some colour values
 46 #define BLACK   0x0000
 47 #define BLUE    0x001F
 48 #define RED     0xF800
 49 #define GREEN   0x07E0
 50 #define CYAN    0x07FF
 51 #define MAGENTA 0xF81F
 52 #define YELLOW  0xFFE0
 53 #define WHITE   0xFFFF
 54 #define ORANGE  0xFC00
 55 #define GREY    0x1863
 56 #define BACKGRD 0x2965
 57 
 58 //RS485 converter connections
 59 #define MAX485_DE   49
 60 #define MAX485_RE   48
 61 
 62 // For Epever AN controller MODBUS read
 63 #define PANEL_VOLTS     0x00
 64 #define PANEL_AMPS      0x01
 65 #define PANEL_POWER_L   0x02
 66 #define PANEL_POWER_H   0x03
 67 #define BATT_VOLTS      0x04
 68 #define BATT_AMPS       0x05
 69 #define BATT_POWER_L    0x06
 70 #define BATT_POWER_H    0x07
 71 #define OVERVOLT_DISC   0x03
 72 #define CHARGING_LIMIT  0x04
 73 #define OVERVOLT_RECON  0x05
 74 #define EQUILIBRIUM_CV  0x06
 75 #define BOOST_CV        0x07
 76 #define FLOAT_CV        0x08
 77 #define BOOST_RECON     0x09
 78 #define LOW_VOLT        0x0A
 79 #define UNDERVOLT_RECOV 0x0B
 80 #define UNDERVOLT_WARN  0x0C
 81 #define LOW_VOLT_DISC   0x0D
 82 #define DISCHARGE_LIMIT 0x0E
 83 
 84 //Touchscreen
 85 #define YP A3  // must be an analog pin, use "An" notation!
 86 #define XM A2  // must be an analog pin, use "An" notation!
 87 #define YM 9   // can be a digital pin
 88 #define XP 8   // can be a digital pin
 89 #define MINPRESSURE 10
 90 #define MAXPRESSURE 1000
 91 #define TS_MINX 906
 92 #define TS_MAXX 116
 93 #define TS_MINY 92
 94 #define TS_MAXY 952
 95 
 96 //Globals
 97 bool buzzer = LOW;
 98 #ifdef ARDUINO_MEGA2560
 99 const int spiCSPin = 53;
100 #endif
101 unsigned long currentMillis;
102 
103 float voltage[NUMPACKS][NUMCELLS];
104 int temperature[NUMPACKS][NUMCELLS];
105 
106 //Pack Parameters - you will fill this info in once you find the CAN address of your packs. Only the last two numbers will be relevant
107 float sum[NUMPACKS];
108 long knownID[NUMPACKS] = {0x00000004, 0x00000020, 0x00000006, 0x00000022};
109 long packID;
110 int slot;
111 int c= 0;
112 int p= 0;
113 float Pack = 0.0;
114 bool msg5 = LOW;
115 float hotCellVolt = 0;
116 float coldCellVolt = 0;
117 
118 
119 //Variables for holding the solar controller's set values
120 unsigned short OVERVOLT_DISC_READ;
121 unsigned short CHARGING_LIMIT_READ;
122 unsigned short OVERVOLT_RECON_READ;
123 unsigned short EQUILIBRIUM_CV_READ;
124 unsigned short BOOST_CV_READ;
125 unsigned short FLOAT_CV_READ;
126 unsigned short BOOST_RECON_READ;
127 unsigned short LOW_VOLT_RECON_READ;
128 unsigned short UNDERVOLT_RECOVERY_READ;
129 unsigned short UNDERVOLT_WARNING_READ;
130 unsigned short LOW_VOLT_DISC_READ;
131 unsigned short DISCHARGE_LIMIT_READ;
132 
133 //Variables for holding register values from the controller
134 unsigned short PANEL_VOLTS_READ;
135 unsigned short PANEL_AMPS_READ;
136 unsigned short BATT_VOLTS_READ;
137 unsigned short BATT_AMPS_READ;
138 
139 //Variables for holding values to be sent to the solar controller
140 unsigned short OVERVOLT_DISC_SETTO;
141 unsigned short CHARGING_LIMIT_SETTO;
142 unsigned short OVERVOLT_RECON_SETTO;
143 unsigned short EQUILIBRIUM_CV_SETTO;
144 unsigned short BOOST_CV_SETTO;
145 unsigned short FLOAT_CV_SETTO;
146 unsigned short BOOST_RECON_SETTO;
147 unsigned short LOW_VOLT_RECON_SETTO;
148 unsigned short UNDERVOLT_RECOVERY_SETTO;
149 unsigned short UNDERVOLT_WARNING_SETTO;
150 unsigned short LOW_VOLT_DISC_SETTO;
151 unsigned short DISCHARGE_LIMIT_SETTO;
152 
153 //Variables for working with the controller
154 bool windDownFlag = LOW;
155 unsigned long unknownId = 0;
156 
157 //Low failure rate timer
158 //const int windDown_period = 60000;
159 unsigned long windDown_etime = 0;
160 //const long windUp_period = 600000;
161 unsigned long windUp_etime = 0;
162 unsigned long dischargeProtect_etime = 0;
163 
164 
165 TouchScreen ts = TouchScreen(XP,YP, XM, YM, 300);
166 MCP_CAN CAN(53);
167 
168 //if the IC model is known or the modules is unreadable,you can use this constructed function
169 LCDWIKI_KBV mylcd(ILI9486,A3,A2,A1,A0,A4); //model,cs,cd,wr,rd,reset
170 //if the IC model is not known and the modules is readable,you can use this constructed function
171 //LCDWIKI_KBV mylcd(320,480,A3,A2,A1,A0,A4);//width,height,cs,cd,wr,rd,reset
172 
173 // instantiate ModbusMaster object
174 ModbusMaster node;
175 
176 /*Function Prototypes
177 void BuildScreen(int);
178 void UpdateScreen(int);
179 void preTransmission();
180 void postTransmission();
181 void getControllerParams();
182 void windDown();
183 void windUp();
184 void dischargeProtect();
185 void updateController();
186 void defaultController();
187 */
188 
189 void setup()
190 {
191 long st, fn;
192 int q;  
193   Serial.begin(115200);
194 
195   //Graphics
196   mylcd.Init_LCD();
197   Serial.print("My LCD ID: ");
198   Serial.println(mylcd.Read_ID(), HEX);
199   BuildScreen(0);
200 
201   //Pin 49 voltage alarm
202   pinMode(49, OUTPUT);
203   //Initialize against undefined values
204   for(int i=0;i<NUMPACKS;i++)
205   {
206     for(int j=0;j<NUMCELLS;j++)
207     {
208       voltage[i][j]=0.0;
209       temperature[i][j]=0;
210     }
211   }
212 
213   while (CAN_OK != CAN.begin(CAN_125KBPS,MCP_8MHz))
214   {
215     Serial.println("CAN BUS Init Failed, waiting for hardware...");
216     delay(100);
217   }
218   Serial.println("CAN BUS  Init OK!");
219 
220   pinMode(MAX485_RE, OUTPUT);
221   pinMode(MAX485_DE, OUTPUT);
222   // Init in receive mode
223   digitalWrite(MAX485_RE, 0);
224   digitalWrite(MAX485_DE, 0);
225   
226   // Modbus setup at 115200 baud
227   Serial1.begin(115200);
228 
229   // EPEver Device ID 1
230   node.begin(1, Serial1);
231 
232   // Callbacks
233   node.preTransmission(preTransmission);
234   node.postTransmission(postTransmission);
235 
236   pinMode(defaultPin, INPUT_PULLUP);
237   if (defaultPin == LOW)
238     defaultController(); //Pin 22, shorted to ground, will default the controller to the settings defined atop
239   else
240   {
241     Serial.println("Controller probed for default values");
242     getControllerParams();
243     //This will read the stores values from the non volatile memory in the controller in the event of a reset.
244     OVERVOLT_DISC_SETTO = OVERVOLT_DISC_READ;
245     CHARGING_LIMIT_SETTO = CHARGING_LIMIT_READ;
246     OVERVOLT_RECON_SETTO = OVERVOLT_RECON_READ;
247     EQUILIBRIUM_CV_SETTO = EQUILIBRIUM_CV_READ;
248     BOOST_CV_SETTO = BOOST_CV_READ;
249     FLOAT_CV_SETTO = FLOAT_CV_READ;
250     BOOST_RECON_SETTO = BOOST_RECON_READ;
251     LOW_VOLT_RECON_SETTO = LOW_VOLT_RECON_READ;
252     UNDERVOLT_RECOVERY_SETTO = UNDERVOLT_RECOVERY_READ;
253     UNDERVOLT_WARNING_SETTO = UNDERVOLT_WARNING_READ;
254     LOW_VOLT_DISC_SETTO = LOW_VOLT_DISC_READ;
255     DISCHARGE_LIMIT_SETTO = DISCHARGE_LIMIT_READ;
256     Serial.println("Default values pulled from controller");
257    }
258 
259 }
260 
261 void loop()
262 {
263   long msgID = 0x00000FFF;
264   bool skip = LOW;
265   unsigned char len = 0;
266   unsigned char buf[10];
267   unsigned long canId = 0x00000000;
268 
269   //WARNING - Touch screen may be blocking
270   digitalWrite(13, HIGH);
271   TSPoint p = ts.getPoint();
272   digitalWrite(13, LOW);
273   pinMode(XM, OUTPUT);
274   pinMode(YP, OUTPUT);
275   if (p.z > MINPRESSURE && p.z < MAXPRESSURE)
276   {
277     //p.x = my_lcd.Get_Display_Width()-map(p.x, TS_MINX, TS_MAXX, my_lcd.Get_Display_Width(), 0);
278     //p.y = my_lcd.Get_Display_Height()-map(p.y, TS_MINY, TS_MAXY, my_lcd.Get_Display_Height(), 0);
279     p.x = map(p.x, TS_MINX, TS_MAXX, mylcd.Get_Display_Width(),0);
280     p.y = map(p.y, TS_MINY, TS_MAXY, mylcd.Get_Display_Height(),0);
281 
282     Serial.print("Touch detect x=");
283     Serial.print(p.x);
284     Serial.print(" y=");
285     Serial.println(p.y);        
286   }
287 
288   if(CAN_MSGAVAIL == CAN.checkReceive())
289   {
290     CAN.readMsgBuf(&len, buf);
291     canId = CAN.getCanId();
292     //Serial.print("p");//packet received
293 
294     if (canId & 0xFFFFF000 != 0x08010000)
295     {
296       Serial.println("Unsupported device attached!  Correct and restart monitor.");
297       Serial.print("Device: 0x");
298       Serial.print(canId & 0xFFFFF000,HEX);
299       Serial.println(".");
300       //Sound alarm
301       digitalWrite(8,1);
302       while(1){}
303     }
304     
305     //detect if pack is registered
306     packID = canId & 0x000000FF;
307     slot = 0xFF;
308     for(int i = 0; i<NUMPACKS ; i++)
309     {
310       if(knownID[i]==packID)
311       {
312         slot=i;
313       }
314       if(i==NUMPACKS-1 && slot==0xFF)
315       {
316         Serial.println("Unregistered Device attached!  Skipping!");
317         Serial.print("  Device : 0x");
318         Serial.print(packID,HEX);
319         Serial.println(".");
320         skip = HIGH;    
321       }
322     }
323     
324     //If pack is registered, collect it's data into local storage.
325     if(!skip)
326     {
327     
328       msgID = canId & 0x00000F00;
329       //Interpret message 3
330       if (msgID == 0x00000300)
331       {        
332         //Copy seven values to temperature[][]
333         if(buf[0]==0)
334         {
335           for(int i=0;i<NUMCELLS-1;i++)
336           {
337             temperature[slot][i]=tempconvert(buf[i+1]); //i+1 because buf[0] is a count register
338           }
339         }
340         //Copy one value to temperature[][0] wich is cell 1.
341         if(buf[0]==1)
342         {
343           temperature[slot][7]=tempconvert(buf[1]);
344         }
345         //unknown temperature code
346         if(buf[0]>1)
347         {
348           Serial.println("Unknown Temperature code");
349         }
350         //for(int i = 0; i<len; i++) {buf3[i] = buf[i];}
351       }
352       //Interpret message 5
353       else if (msgID == 0x00000500) ;//{for(int i = 0; i<len; i++){buf5[i] = buf[i];}}
354       //Interpret message A
355       else if (msgID == 0x00000A00) ;//{for(int i = 0; i<len; i++){bufA[i] = buf[i];}}
356       //Interpret message 0
357       else if (msgID == 0x00000000) ;//{for(int i = 0; i<len; i++){buf0[i] = buf[i];}}
358       //Interpret message 2
359       else if (msgID == 0x00000200)
360       {
361         if (buf[0] == 0)
362         {
363           voltage[slot][7] = (buf[2] * 256 + buf[1]) / 1000.0;
364           voltage[slot][6] = (buf[4] * 256 + buf[3]) / 1000.0;
365           voltage[slot][5] = (buf[6] * 256 + buf[5]) / 1000.0;
366           //for(int i = 0; i<len; i++){buf20[i] = buf[i];} 
367         }
368         if (buf[0] == 1)
369         {
370           voltage[slot][4] = (buf[2] * 256 + buf[1]) / 1000.0;
371           voltage[slot][3] = (buf[4] * 256 + buf[3]) / 1000.0;
372           voltage[slot][2] = (buf[6] * 256 + buf[5]) / 1000.0;
373           //for(int i = 0; i<len; i++){buf21[i] = buf[i];}
374         }
375         if (buf[0] == 2)
376         {
377           voltage[slot][1] = (buf[2] * 256 + buf[1]) / 1000.0;
378           voltage[slot][0] = (buf[4] * 256 + buf[3]) / 1000.0;
379           //for(int i = 0; i<len; i++){buf22[i] = buf[i];}
380         }
381       }
382       else 
383       {
384         unknownId = canId;
385         Serial.println("Unexpected Message type detected!");
386         Serial.println(canId & 0x00000F00, HEX);
387       }
388       
389       hotCellVolt = voltage[0][0];
390       coldCellVolt = voltage[0][0];
391       for(int i=0; i<NUMPACKS;i++) //Calculate a pack sum and check for hot cells
392       {
393         sum[i]=0.0;
394         for(int j=0;j<NUMCELLS;j++)
395         {
396           sum[i] += voltage[i][j];
397           if (voltage[i][j] > hotCellVolt)
398             hotCellVolt = voltage[i][j];
399           else if (voltage[i][j] < coldCellVolt)
400             coldCellVolt = voltage[i][j];
401           if(hotCellVolt >= ALARM)
402           {
403             buzzer=HIGH;
404           }
405           if((coldCellVolt <= LOWCELL) && (coldCellVolt >= 1))
406           {
407             dischargeProtect();
408           }
409           
410         }  
411         
412       }
413         
414       //Execute alarm condition
415       currentMillis = millis();
416       if(buzzer)
417       {
418         digitalWrite(49,1);
419         windDown(); // This loop should only be executed once and then skipped for at least 1 minute
420         windDown_etime = currentMillis;
421         Serial.println("Buzzer Loop");
422       } else digitalWrite(49,0);        
423       if (windDownFlag && ((unsigned long)(currentMillis - windDown_etime) >= windDown_period))
424         {
425           Serial.println("post windDown windUp triggered");
426           windUp();
427           windUp_etime = currentMillis;
428         }
429       if ((PANEL_VOLTS_READ >= dayLightVolt) && (PANEL_AMPS_READ <= 1) && (hotCellVolt <= windUpThreshold) && ((unsigned long)(currentMillis - windUp_etime) >= windUp_period)) //Keep tweaking the controller to max
430         {
431           Serial.println("conditional windUp triggered");
432           windUp();
433           windUp_etime = currentMillis;
434         }
435         
436         //Dump cell voltages to CSV
437         /*
438         Pack=0.0;
439         for(int i=0; i<NUMPACKS;i++)
440         {
441           Pack=0.0;
442           for(int j=0; j<NUMCELLS ; j++) 
443           {
444 
445             Serial.print(voltage[i][j],3);
446             Serial.print(",");
447 
448 
449             Pack += voltage[i][j];
450 
451           }
452           Serial.print(Pack,3);
453           if(NUMPACKS-1!=i)
454             Serial.print(',');
455           else
456             Serial.println();     
457         }
458         */
459         //END Dump cell voltages to CSV
460         
461       if(unknownId != 0)
462       {
463         Serial.print("Unknown Message: ");
464         Serial.print(unknownId, HEX);
465         Serial.println();
466       }
467      }
468      else skip=LOW;    
469     }
470     else
471     {
472       UpdateScreen(0);
473     }
474 }
475 
476 void BuildScreen(int ScreenMode)
477 {
478   //Home Screen
479   if(ScreenMode==0)
480   {
481     mylcd.Fill_Screen(BACKGRD);
482 
483     //Draw Blues
484     mylcd.Set_Draw_color(32,0,255);
485     mylcd.Fill_Rectangle(0, 0, mylcd.Get_Display_Width()-1, 26);
486 
487     //Draw Blacks
488     mylcd.Set_Draw_color(0,0,0);
489     mylcd.Fill_Rectangle(10,42 , 150, 191);   //Pack 1
490     mylcd.Fill_Rectangle(170,42 , 310, 191);//Pack 2
491     mylcd.Fill_Rectangle(10,234 , 150, 386);//Pack 3
492     mylcd.Fill_Rectangle(170, 234 , 310, 386);//Pack 4
493 
494     //Draw Lines
495     mylcd.Set_Draw_color(WHITE);
496     mylcd.Draw_Line(11, 62, 149, 62);//Pack 1
497     mylcd.Draw_Line(171, 62, 309, 62);//Pack 2
498     mylcd.Draw_Line(11, 254, 149, 254);//Pack 3
499     mylcd.Draw_Line(171, 254, 309, 254);//Pack 4
500 
501     //Draw Reds 
502     mylcd.Set_Draw_color(RED);
503     mylcd.Fill_Rectangle(10,193 , 150, 214);//Pack 1
504     mylcd.Fill_Rectangle(170,193, 310, 214);//Pack 2
505     mylcd.Fill_Rectangle(10,384 , 150, 407);//Pack 3
506     mylcd.Fill_Rectangle(170,384, 310, 407);//Pack 4
507     //Title
508     mylcd.Set_Text_colour(CYAN);
509     mylcd.Set_Text_Mode(1);
510     mylcd.Set_Text_Size(3);
511     mylcd.Print_String("Battery Monitor", 30, 0);
512     //Outline the screen
513     mylcd.Set_Text_Size(2);
514     mylcd.Set_Text_Back_colour(BLACK);
515     mylcd.Set_Text_colour(WHITE);
516     mylcd.Print_String("  x04    (-)   x20    (-)", 4, 30+16*1);
517     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*2);
518     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*3);
519     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*4);
520     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*5);
521     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*6);
522     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*7);
523     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*8);
524     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*9);
525     mylcd.Print_String(" (+)       V  (+)       V", 4, 38+16*10);
526     mylcd.Print_String("  x06    (-)   x22    (-)", 4, 30+16*13);
527     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*14);
528     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*15);
529     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*16);
530     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*17);
531     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*18);
532     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*19);
533     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*20);
534     mylcd.Print_String(" -.---V  --F  -.---V  --F", 4, 32+16*21);
535     mylcd.Print_String(" (+)       V  (+)       V", 4, 38+16*22);
536 
537     //credits, statusbar
538     mylcd.Set_Text_colour(CYAN);
539     mylcd.Print_String("Caleb Box and Timothy Legg", 4, 32+16*27);
540   }
541   return;
542 }
543 
544 void UpdateScreen(int battery)
545 {
546   
547   int xc, yc;
548   if(p==0 || p==1)
549   {
550     yc=64+(16*c);
551   }
552   if(p==2 || p==3)
553   {
554     yc=256+(16*c);
555   }
556   if(p==0 || p==2)
557   {
558     xc=14;
559   }
560   if(p==1 || p==3)
561   {
562     xc=172;
563   }
564   mylcd.Set_Text_Mode(0);
565   mylcd.Set_Text_Back_colour(BLACK);
566   mylcd.Set_Text_colour(GREEN);
567   if(voltage[p][c]> 3.600 || voltage[p][c] < 3.200) mylcd.Set_Text_colour(YELLOW);
568   if(voltage[p][c]> 3.645 || voltage[p][c] < 3.150) mylcd.Set_Text_colour(ORANGE);
569   if(voltage[p][c]< 2.900 || voltage[p][c] > 3.650) mylcd.Set_Text_colour(RED);
570   if(voltage[p][c]<= 0.0001) mylcd.Set_Text_colour(GREY);
571 
572   if(p!=4) mylcd.Print_Number_Float(voltage[p][c], 3, xc,yc, '.', 0, ' ');
573   if(p!=4) mylcd.Print_Number_Int(temperature[p][c], xc+96,yc,2, ' ', 10);
574   
575   if(c==7)
576   {
577     mylcd.Set_Text_Mode(0);
578     mylcd.Set_Text_colour(WHITE);
579     mylcd.Set_Text_Back_colour(RED);
580     mylcd.Print_Number_Float(sum[p], 3, xc+48, yc+22, '.', 0, ' ');
581     mylcd.Set_Text_Back_colour(BLACK);
582   }
583   if(c<NUMCELLS){c++;}
584   if(c==NUMCELLS){p++;c=0;}
585   if(p==NUMPACKS){p=0;}
586   buzzer=LOW;
587   return;
588 }
589 char tempconvert(char raw) // Convert the readings from the ADC to human readable
590 {
591   //Fahrenheit this loop will calculate it live but the look up table saves clock cycles
592   //Supports Rounding
593   //if( (raw*1.555)-26.0 - (int) ((raw*1.555)-26.0) >= 0.5)
594   //  return (int)((raw*1.555)-25.0);
595   //else
596   //  return (int)((raw*1.555)-26.0);
597   short static const table[256] = 
598                      {-26, -24,-23,-21,-20,-18,-17,-15,-14,-12,-10,-9,-7,-6,-4,-3,-1,
599                       0,2,4,5,7,8,10,11,13,14,16,18,19,21,22,24,25,27,28,30,32,33,35,
600                       36,38,39,41,42,44,46,47,49,50,52,53,55,56,58,60,61,63,64,66,67,
601                       69,70,72,74,75,77,78,80,81,83,84,86,88,89,91,92,94,95,97,98,100,
602                       102,103,105,106,108,109,111,112,114,116,117,119,120,122,123,125,
603                       126,128,130,131,133,134,136,137,139,140,142,143,145,147,148,150,
604                       151,153,154,156,157,159,161,162,164,165,167,168,170,171,173,175,
605                       176,178,179,181,182,184,185,187,189,190,192,193,195,196,198,199,
606                       201,203,204,206,207,209,210,212,213,215,217,218,220,221,223,224,
607                       226,227,229,231,232,234,235,237,238,240,241,243,245,246,248,249,
608                       251,252,254,255,257,259,260,262,263,265,266,268,269,271,273,274,
609                       276,277,279,280,282,283,285,287,288,290,291,293,294,296,297,299,
610                       301,302,304,305,307,308,310,311,313,315,316,318,319,321,322,324,
611                       325,327,329,330,332,333,335,336,338,339,341,343,344,346,347,349,
612                       350,352,353,355,357,358,360,361,363,364,366,367,369,371};
613   return (int)table[raw]; 
614 }
615 
616 void preTransmission() //Put RS485 into transmit mode by bringing the transmit and receive pins high
617 {
618   digitalWrite(MAX485_RE, 1);
619   digitalWrite(MAX485_DE, 1);
620 }
621 
622 void postTransmission() //Put the RS485 into receive mode by bringing the transmit and receive pins low
623 {
624   digitalWrite(MAX485_RE, 0);
625   digitalWrite(MAX485_DE, 0);
626 }
627 void getControllerParams() // Hit the controller with a request for information
628 {
629 uint8_t result;
630 
631   // Read input registers from the controller starting at address 0x3100)
632   node.clearResponseBuffer();
633   result = node.readInputRegisters(0x3100, 5);
634 
635   if (result == node.ku8MBSuccess)
636   {
637     PANEL_VOLTS_READ = node.getResponseBuffer(PANEL_VOLTS); //Data slot 0
638     PANEL_AMPS_READ = node.getResponseBuffer(PANEL_AMPS); //Data slot 1
639     BATT_VOLTS_READ = node.getResponseBuffer(BATT_VOLTS); //Data slot 4 (2,3 skipped, not needed)
640     Serial.print("Panel volts: ");
641     Serial.println((float)PANEL_VOLTS_READ/100.00);
642     Serial.print("Panel Amps: ");
643     Serial.println((float)PANEL_AMPS_READ/100.00);
644     Serial.print("Battery volts: ");
645     Serial.println((float)BATT_VOLTS_READ/100.00);
646     Serial.println();
647   } 
648   else { //If there's an error send it to the console
649     Serial.print("Reading controller parameters failed, error:");
650     Serial.println(result);
651   }
652   //delay(1000);
653 
654   // Read charging parameters from the controller starting at address 0x9000)
655   node.clearResponseBuffer();
656   result = node.readHoldingRegisters(0x9000, 15);
657 
658   if (result == node.ku8MBSuccess)
659   {
660     //Some values are not collected because we will always use the defaults
661     OVERVOLT_DISC_READ = node.getResponseBuffer(OVERVOLT_DISC);
662     CHARGING_LIMIT_READ = node.getResponseBuffer(CHARGING_LIMIT);
663     OVERVOLT_RECON_READ = node.getResponseBuffer(OVERVOLT_RECON);
664     EQUILIBRIUM_CV_READ = node.getResponseBuffer(EQUILIBRIUM_CV);
665     BOOST_CV_READ = node.getResponseBuffer(BOOST_CV);
666     FLOAT_CV_READ = node.getResponseBuffer(FLOAT_CV);
667     BOOST_RECON_READ = node.getResponseBuffer(BOOST_RECON);
668     LOW_VOLT_RECON_READ = node.getResponseBuffer(LOW_VOLT);
669     UNDERVOLT_RECOVERY_READ = node.getResponseBuffer(UNDERVOLT_RECOV);
670     UNDERVOLT_WARNING_READ = node.getResponseBuffer(UNDERVOLT_WARN);
671     LOW_VOLT_DISC_READ = node.getResponseBuffer(LOW_VOLT_DISC);
672     DISCHARGE_LIMIT_READ = node.getResponseBuffer(DISCHARGE_LIMIT);
673     Serial.println("Parameters received successfully");
674   } 
675   else {
676     Serial.print("The charging parameters failed to read, error: ");
677     Serial.println(result);
678   }
679 
680   //delay(2000);
681 }
682 
683 
684 void windDown() // Bring the controller charge voltage down so the hot cell will kalm
685 {
686   OVERVOLT_DISC_SETTO = BATT_VOLTS_READ + 50;
687   CHARGING_LIMIT_SETTO = BATT_VOLTS_READ - 20;
688   OVERVOLT_RECON_SETTO = BATT_VOLTS_READ - 30;
689   EQUILIBRIUM_CV_SETTO = BATT_VOLTS_READ - 30;
690   BOOST_CV_SETTO = BATT_VOLTS_READ - 40;
691   FLOAT_CV_SETTO = BATT_VOLTS_READ - 50;
692   BOOST_RECON_SETTO = BATT_VOLTS_READ - 200;
693   updateController();
694   windDownFlag = HIGH;
695   Serial.println("Wind down has been prepared for sending");
696   //Insert a timer to wind up in 2 minutes
697 }
698 void windUp() // Bring the controller charge voltage as close to max as possible
699 {
700   Serial.println("Windup activated");
701   getControllerParams();
702   unsigned short difference = int((ALARM - hotCellVolt) * 100);
703   unsigned short setvolt = BATT_VOLTS_READ + difference;
704   if (difference > 10)
705   {
706     OVERVOLT_DISC_SETTO = (setvolt + 50);
707     CHARGING_LIMIT_SETTO = setvolt;
708     OVERVOLT_RECON_SETTO = setvolt - 10;
709     EQUILIBRIUM_CV_SETTO = setvolt - 20;
710     BOOST_CV_SETTO = setvolt - 30;
711     FLOAT_CV_SETTO = setvolt - 40;
712     BOOST_RECON_SETTO = setvolt - 100;
713     Serial.println("Wind Up has been prepared for sending");
714     updateController();
715     windDownFlag = LOW;
716   }
717   
718 }
719 void dischargeProtect()
720 {
721   
722   getControllerParams();
723   currentMillis = millis();
724   if (((DISCHARGE_LIMIT_READ + 60) > BATT_VOLTS_READ) && (unsigned long)(currentMillis - dischargeProtect_etime) >= dischargeProtect_period)
725   {
726     UNDERVOLT_RECOVERY_SETTO = BATT_VOLTS_READ + 50;
727     UNDERVOLT_WARNING_SETTO = BATT_VOLTS_READ + 30;
728     LOW_VOLT_RECON_SETTO = BATT_VOLTS_READ + 20;
729     LOW_VOLT_DISC_SETTO = BATT_VOLTS_READ - 20;
730     DISCHARGE_LIMIT_SETTO = BATT_VOLTS_READ - 50;
731     Serial.println("Overdischarge protection adjustment prepared");
732     updateController();
733     dischargeProtect_etime = currentMillis;
734   }
735 }
736 void updateController() // Send new information to the controller. Something unusual discovered with the Epever AN controller is that it will not change 9003-9007 
737 {
738   
739   node.clearTransmitBuffer(); //Prepare the buffer with new data, clear and fill
740   node.setTransmitBuffer(0, def_BATT_TYPE);
741   node.setTransmitBuffer(1, def_BATT_AH);
742   node.setTransmitBuffer(2, def_TEMP_COMP);
743   node.setTransmitBuffer(3, OVERVOLT_DISC_SETTO);
744   node.setTransmitBuffer(4, CHARGING_LIMIT_SETTO);
745   node.setTransmitBuffer(5, OVERVOLT_RECON_SETTO);
746   node.setTransmitBuffer(6, EQUILIBRIUM_CV_SETTO);
747   node.setTransmitBuffer(7, BOOST_CV_SETTO);
748   node.setTransmitBuffer(8, FLOAT_CV_SETTO);
749   node.setTransmitBuffer(9, BOOST_RECON_SETTO);
750   node.setTransmitBuffer(10, LOW_VOLT_RECON_SETTO);
751   node.setTransmitBuffer(11, UNDERVOLT_RECOVERY_SETTO);
752   node.setTransmitBuffer(12, UNDERVOLT_WARNING_SETTO);
753   node.setTransmitBuffer(13, LOW_VOLT_DISC_SETTO);
754   node.setTransmitBuffer(14, DISCHARGE_LIMIT_SETTO);
755   
756   int result = (node.writeMultipleRegisters(0x9000,15)); //Yeet this new info to the controller
757   if (result == 0) //Verify
758     Serial.print("Controller parameters 9000-900F changed sucessfully"); // For Diagnostic console
759   else
760   {
761     Serial.print("Controller parameters 9000-900F change failed, error: ");
762     Serial.println(result);
763   }
764 }
765 void defaultController()
766 {
767   node.clearTransmitBuffer(); //Prepare the buffer with new data, clear and fill
768   node.setTransmitBuffer(0, def_BATT_TYPE);
769   node.setTransmitBuffer(1, def_BATT_AH);
770   node.setTransmitBuffer(2, def_TEMP_COMP);
771   node.setTransmitBuffer(3, def_OVERVOLT_DISC_VAL);
772   node.setTransmitBuffer(4, def_CHARGING_LIMIT_VAL);
773   node.setTransmitBuffer(5, def_OVERVOLT_RECON_VAL);
774   node.setTransmitBuffer(6, def_EQUILIBRIUM_CV_VAL);
775   node.setTransmitBuffer(7, def_BOOST_CV_VAL);
776   node.setTransmitBuffer(8, def_FLOAT_CV_VAL);
777   node.setTransmitBuffer(9, def_BOOST_RECON_VAL);
778   node.setTransmitBuffer(10, def_LOW_VOLT_RECON);
779   node.setTransmitBuffer(11, def_UNDERVOLT_RECOV);
780   node.setTransmitBuffer(12, def_UNDERVOLT_WARNING);
781   node.setTransmitBuffer(13, def_LOW_VOLT_DISC);
782   node.setTransmitBuffer(14, def_DISCHARGE_LIMIT);
783   
784   int result = (node.writeMultipleRegisters(0x9000,15)); //Yeet this new info to the controller
785   if (result == 0) //Verify
786     Serial.print("Controller parameters 9000-900F have been set to default"); // For Diagnostic console
787   else
788   {
789     Serial.print("Controller parameters 9000-900F default failed, error: ");
790     Serial.println(result);
791   }
792 }