CAN Battery Monitor with Epever Controller Integration + 3.5TFT Mega2560
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 }