-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathScullCom_DCLoad.ino
1459 lines (1214 loc) · 54.2 KB
/
ScullCom_DCLoad.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//SCULLCOM HOBBY ELECTRONICS
//ELECTRONIC DC LOAD PROJECT
//Software Version 35 (4x5 Matrix Keypad Version - modified key layout)
//14th April 2018
#include <SPI.h> //include SPI library (Serial Peripheral Interface)
#include <Wire.h> //include I2C library
#include <LiquidCrystal_I2C.h> // F Malpartida's NewLiquidCrystal library
// https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/NewliquidCrystal_1.3.4.zip
#include <math.h> //
#include <Adafruit_MCP4725.h> //Adafruit DAC library https://github.com/adafruit/Adafruit_MCP4725
#include <MCP342X.h> //uChip library avaiable from https://github.com/uChip/MCP342X
#include <MCP79410_Timer.h> //Scullcom Hobby Electronics library http://www.scullcom.com/MCP79410Timer-master.zip
#include <EEPROM.h> //include EEPROM library used for storing setup data
#include <Keypad.h> //http://playground.arduino.cc/Code/Keypad
const byte ROWS = 5; //five rows
const byte COLS = 4; //four columns
//define the symbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
{'<','0','>','F'},
{'7','8','9','E'},
{'4','5','6','D'},
{'1','2','3','C'},
{'A','B','#','*'}
};
byte rowPins[ROWS] = {9, 10, 11, 12, 14}; //connect to the row pin outs of the keypad
byte colPins[COLS] = {5, 6, 7, 8}; //connect to the column pin outs of the keypad
//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
char customKey;
char decimalPoint; //used to test for more than one press of * key (decimal point)
Adafruit_MCP4725 dac; //constructor
MCP342X adc;
const byte MCP79410_ADDRESS = 0x6f; //0x6f is the default address for the MCP79410 Real Time Clock IC
MCP79410_Timer timer = MCP79410_Timer(MCP79410_ADDRESS);
//Set the pins on the I2C chip used for LCD connections
//ADDR,EN,R/W,RS,D4,D5,D6,D7
LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7); //0x27 is the default address of the LCD with I2C bus module
const byte pinA = 2; //digital pin (also interrupt pin) for the A pin of the Rotary Encoder (changed to digital pin 2)
const byte pinB = 4; //digital pin for the B pin of the Rotary Encoder
const byte CursorPos = 17; //analog pin A3 used as a digital pin to set cursor position (rotary encoder push button)
const byte LoadOnOff = 15; //analog pin A1 used as a digital pin to set Load ON/OFF
const byte TriggerPulse = 16; //analog pin A2 used as a digital pin for trigger pulse input in transient mode
const byte fan = 3; //digital pin 3 for fan control output (changed to Digital pin 3)
const byte temperature = A6; //analog pin used for temperature output from LM35 (was A0 previously but changed)
int tempmin = 30;
int tempmax = 35;
int temp; //
float BatteryLife = 0; //
float BatteryLifePrevious = 0; //
float Seconds = 0; //time variable used in Battery Capacity Mode (BC)
float SecondsLog = 0; //variable used for data logging of the time in seconds
float BatteryCutoffVolts; //used to set battery discharge cut-off voltage
int BatteryNumOfCells = 1; //number of cell in battery discharge preset mode
float MaxBatteryCurrent = 4.0; //maximum battery current allowed for Battery Capacity Testing
int stopSeconds; //store for seconds when timer stopped
int CP = 8; //cursor start position
boolean toggle = false; //used for toggle of Load On/Off button
unsigned long controlVoltage = 0; //used for DAC to control MOSFET
long current = 0; //variable used by ADC for measuring the current
long voltage = 0; //variable used by ADC for measuring the voltage
float reading = 0; //variable for Rotary Encoder value divided by 1000
float setCurrent = 0; //variable used for the set current of the load
float setPower = 0; //variable used for the set power of the load
float setResistance = 0; //variable used for the set resistance of the load
float setCurrentCalibrationFactor = 0.980; //calibration adjustment - set as required if needed (was 1.000)
float displayCurrentCal = -0.004; //calibration correction for LCD current display (was 0.040)
int Load = 0; //Load On/Off flag
float setControlCurrent = 0; //variable used to set the temporary store for control current required
int VoltsDecimalPlaces = 3; //number of decimal places used for Voltage display on LCD
float ActualVoltage = 0; //variable used for Actual Voltage reading of Load
float ActualCurrent = 0; //variable used for Actual Current reading of Load
float ActualPower = 0; //variable used for Actual Power reading of Load
float ResistorCutOff = 999; //maximum Resistor we want to deal with in software
float BatteryCurrent; //
float LoadCurrent; //
int CurrentCutOff = EEPROM.read(0x00);
int PowerCutOff = EEPROM.read(0x20);
int tempCutOff = EEPROM.read(0x40);
int setReading = 0; //
int ControlVolts = 0; //used to set output current
float OutputVoltage = 0; //
String Mode =" "; //used to identify which mode
int modeSelected = 0; //Mode status flag
int lastCount = 50; //
volatile float encoderPosition = 0; //
volatile unsigned long factor= 0; //number of steps to jump
volatile unsigned long encoderMax = 999000; //sets maximum Rotary Encoder value allowed CAN BE CHANGED AS REQUIRED (was 50000)
float LiPoCutOffVoltage = 3.0; //set cutoff voltage for LiPo battery
float LiFeCutOffVoltage = 2.8; //set cutoff voltage for LiFe battery
float NiCdCutOffVoltage = 1.0; //set cutoff voltage for NiCd battery
float ZiZnCutOffVoltage = 1.0; //set cutoff voltage for ZiZn battery
float PbAcCutOffVoltage = 1.75; //set cutoff voltage for PbAc battery
String BatteryType =" ";
byte exitMode = 0; //used to exit battery selection menu and return to CC Mode
char numbers[10]; //keypad number entry - Plenty to store a representation of a float
byte index = 0;
int z = 1; //was previously 0
float x = 0;
int y = 0;
int r = 0;
float LowCurrent = 0; //the low current setting for transcient mode
float HighCurrent = 0; //the high current setting for transcient mode
unsigned long transientPeriod; //used to store pulse time period in transcient pulse mode
unsigned long current_time; //used to store the current time in microseconds
unsigned long last_time; //used to store the time of the last transient switch in micro seconds
boolean transient_mode_status; //used to maintain the state of the trascient mode (false = low current, true = high current)
float transientList [10][2]; //array to store Transient List data
int total_instructions; //used in Transient List Mode
int current_instruction; //used in Transient List Mode
//--------------------------------Interrupt Routine for Rotary Encoder------------------------
void isr()
{
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
if (interruptTime - lastInterruptTime > 5) { //
if (digitalRead(pinB) == LOW)
{
encoderPosition = encoderPosition - factor;
}else{
encoderPosition = encoderPosition + factor;
}
encoderPosition = min(encoderMax, max(0, encoderPosition)); // sets maximum range of rotary encoder
lastInterruptTime = interruptTime;
}
}
//---------------------------------Initial Set up---------------------------------------
void setup() {
Serial.begin(9600); //used for testing only
Wire.begin(); //join i2c bus (address optional for master)
Wire.setClock(400000L); //sets bit rate to 400KHz
pinMode (pinA, INPUT);
pinMode (pinB, INPUT);
pinMode (CursorPos, INPUT_PULLUP);
pinMode (LoadOnOff, INPUT_PULLUP);
pinMode (TriggerPulse, INPUT_PULLUP);
pinMode (fan, OUTPUT);
TCCR2B = (TCCR2B & 0b11111000) | 1; //change PWM to above hearing (Kenneth Larvsen recommendation)
pinMode (temperature, INPUT);
analogReference(INTERNAL); //use Arduino internal reference for tempurature monitoring
attachInterrupt(digitalPinToInterrupt(pinA), isr, LOW);
dac.begin(0x61); //the DAC I2C address with MCP4725 pin A0 set high
dac.setVoltage(0,false); //reset DAC to zero for no output current set at Switch On
lcd.begin(20, 4); //set up the LCD's number of columns and rows
lcd.setBacklightPin(3,POSITIVE); // BL, BL_POL
lcd.setBacklight(HIGH); //set LCD backlight on
lcd.clear(); //clear LCD display
lcd.setCursor(6,0); //set LCD cursor to column 0, row 4
lcd.print("SCULLCOM"); //print SCULLCOM to display with 5 leading spaces (you can change to your own)
lcd.setCursor(1,1); //set LCD cursor to column 0, row 1 (start of second line)
lcd.print("Hobby Electronics"); //print Hobby Electronics to display (you can change to your own)
lcd.setCursor(1,2);
lcd.print("DC Electronic Load"); //DC Electronic Load
lcd.setCursor(1,3);
lcd.print("Ver. 38P(Mod.)"); //Version
delay(3000); //3000 mSec delay for intro display
lcd.clear(); //clear dislay
setupLimits();
delay(3000);
lcd.clear();
last_time = 0; //set the last_time to 0 at the start (Transicent Mode)
transient_mode_status = false; //set the initial transient mode status (false = low, true = high);
setCurrent = LowCurrent; //first set the current to the low current value (Transicent Mode)
lcd.setCursor(8,0);
lcd.print("OFF"); //indicate that LOAD is off at start up
Current(); //sets initial mode to be CC (Constant Current) at Power Up
customKey = customKeypad.getKey();
//Check temp on Startup, if higher than lower limit, turn on fan
//This is needed due isteresi control
temp = analogRead(temperature);
temp = analogRead(temperature);
temp = analogRead(temperature);
temp = analogRead(temperature);
temp = temp * 0.107421875;
if (temp > tempmin) {
digitalWrite(fan, HIGH);
}
}
//------------------------------------------Main Program Loop---------------------------------
void loop() {
if(CurrentCutOff > 5){ //Test and go to user set limits if required
userSetUp();
}else{
readKeypadInput(); //read Keypad entry
if (digitalRead(LoadOnOff) == LOW) {
LoadSwitch(); //Load on/off
}
transient(); //test for Transient Mode
lcd.setCursor(18,3); //sets display of Mode indicator at bottom right of LCD
lcd.print(Mode); //display mode selected on LCD (CC, CP, CR or BC)
if(Mode != "TC" && Mode != "TP" && Mode != "TT" && Mode != "TL"){ //if NOT transient mode then Normal Operation
reading = encoderPosition/1000; //read input from rotary encoder
maxConstantCurrentSetting(); //set maxiumum Current allowed in Constant Current Mode (CC)
powerLevelCutOff(); //Check if Power Limit has been exceeded
temperatureCutOff(); //check if Maximum Temperature is exceeded
batteryCurrentLimitValue(); //Battery Discharge Constant Current Limit Value in BC Mode
displayEncoderReading(); //display rotary encoder input reading on LCD
lastCount = encoderPosition; //store rotary encoder current position
CursorPosition(); //check and change the cursor position if cursor button pressed
}else{
transientLoadToggle(); //Start Transient Mode
}
readVoltageCurrent(); //routine for ADC's to read actual Voltage and Current
ActualReading(); //Display actual Voltage, Current readings and Actual Wattage
dacControl();
dacControlVoltage(); //sets the drive voltage to control the MOSFET
batteryCapacity(); //test if Battery Capacity (BC) mode is selected - if so action
fanControl(); //call heatsink fan control
}
}
//------------------------------------------------Read Keypad Input-----------------------------------------------------
void readKeypadInput (void) {
customKey = customKeypad.getKey();
// if (customKey != NO_KEY){ //only used for testing keypad (uncomment if required for testing keypad)
// Serial.print("customKey = "); //only used for testing keypad (uncomment if required for testing keypad)
// Serial.println(customKey); //only used for testing keypad (uncomment if required for testing keypad)
// } //only used for testing keypad (uncomment if required for testing keypad)
if(customKey == 'E'){ //check for Load on/off
LoadSwitch();
}
if(customKey == 'D'){ //check if Set-Up Mode Selected
delay(200);
toggle = false; //switch Load OFF
userSetUp();
encoderPosition = 0; //reset encoder reading to zero
index = 0;
z = 1; //sets column position for LCD displayed character
decimalPoint = (' '); //clear decimal point text character reset
}
if(customKey == 'C'){ //check if Transient Mode Selected
toggle = false; //switch Load OFF
transientType();
encoderPosition = 0; //reset encoder reading to zero
index = 0;
z = 1; //sets column position for LCD displayed character
decimalPoint = (' '); //clear decimal point text character reset
}
if(customKey == 'A'){ //check if Constant Current button pressed
toggle = false; //switch Load OFF
lcd.setCursor(8,0);
lcd.print("OFF");
Current(); //if selected go to Constant Current Selected routine
encoderPosition = 0; //reset encoder reading to zero
index = 0;
z = 1; //sets column position for LCD displayed character
decimalPoint = (' '); //clear decimal point test character reset
}
if(customKey == 'B'){ //check if Constant Power button pressed
toggle = false; //switch Load OFF
lcd.setCursor(8,0);
lcd.print("OFF");
Power(); //if selected go to Constant Power Selected routine
encoderPosition = 0; //reset encoder reading to zero
index = 0;
z = 1; //sets column position for LCD displayed character
decimalPoint = (' '); //clear decimal point test character reset
}
if(customKey == '#'){ //check if Constant Resistance button pressed
toggle = false; //switch Load OFF
lcd.setCursor(8,0);
lcd.print("OFF");
Resistance(); //if selected go to Constant Resistance Selected routine
encoderPosition = 0; //reset encoder reading to zero
index = 0;
z = 1; //sets column position for LCD displayed character
decimalPoint = (' '); //clear decimal point test character reset
}
if(customKey == '*'){ //check if Battery Capacity button pressed
dac.setVoltage(0,false); //Ensures Load is OFF - sets DAC output voltage to 0
toggle = false; //switch Load OFF
batteryType(); //select battery type
index = 0;
z = 1; //sets column position for LCD displayed character
decimalPoint = (' '); //clear decimal point test character reset
if (exitMode == 1){ //if NO battery type selected revert to CC Mode
lcd.setCursor(8,0);
lcd.print("OFF");
Current(); //if selected go to Constant Current Selected routine
encoderPosition = 0; //reset encoder reading to zero
customKey = 'A';
} else {
lcd.setCursor(16,2);
lcd.print(BatteryType); //print battery type on LCD
lcd.setCursor(8,0);
lcd.print("OFF");
timer.reset(); //reset timer
BatteryLifePrevious = 0;
CP = 9; //set cursor position
BatteryCapacity(); //go to Battery Capacity Routine
}
}
if (Mode == "BC"){ //In battery mode, use clear input to reset statistics
if(customKey == '<'){ //clear input entry
//Clear battery statistics and timer
BatteryLife = 0;
BatteryLifePrevious = 0;
lcd.setCursor(9,3);
lcd.print("0000");
timer.reset();
lcd.setCursor(0,3);
lcd.print (timer.getTime());
}
}
if (Mode != "BC"){
if(customKey >= '0' && customKey <= '9'){ //check for keypad number input
numbers[index++] = customKey;
numbers[index] = '\0';
lcd.setCursor(z,3); //
lcd.print(customKey); //show number input on LCD
z = z+1;
}
if(customKey == '>'){ //check if decimal button key pressed
if (decimalPoint != ('>')){ //test if decimal point entered twice - if so skip
numbers[index++] = '.';
numbers[index] = '\0';
lcd.setCursor(z,3);
lcd.print(".");
z = z+1;
decimalPoint = ('>'); //used to indicate decimal point has been input
}
}
if(customKey == '<'){ //clear input entry
index = 0;
z = 1; //sets column position for LCD displayed character
lcd.setCursor(z,3);
lcd.print(" ");
numbers[index] = '\0'; //
decimalPoint = (' '); //clear decimal point test character reset
}
if(customKey == 'F') { //use entered number
x = atof(numbers);
reading = x;
encoderPosition = reading*1000;
index = 0;
numbers[index] = '\0';
z = 1; //sets column position for LCD displayed character
lcd.setCursor(0,3);
lcd.print(" ");
decimalPoint = (' '); //clear decimal point test character reset
}
}
}
//----------------------Limit Maximum Current Setting-----------------------------------------
void maxConstantCurrentSetting (void) {
if (Mode == "CC" && reading > CurrentCutOff){ //Limit maximum Current Setting
reading = CurrentCutOff;
encoderPosition = (CurrentCutOff * 1000); //keep encoder position value at maximum Current Limit
lcd.setCursor(0,3);
lcd.print(" "); //20 spaces to clear last line of LCD
}
if (Mode == "CP" && reading > PowerCutOff) { //Limit maximum Current Setting
reading = PowerCutOff;
encoderPosition = (PowerCutOff * 1000); //keep encoder position value at maximum Current Limit
lcd.setCursor(0,3);
lcd.print(" "); //20 spaces to clear last line of LCD
}
if (Mode == "CR" && reading > ResistorCutOff ) { //Limit maximum Current Setting
reading = ResistorCutOff;
encoderPosition = (ResistorCutOff * 1000); //keep encoder position value at maximum Current Limit
lcd.setCursor(0,3);
lcd.print(" "); //20 spaces to clear last line of LCD
}
}
//----------------------Power Level Cutoff Routine-------------------------------------------
void powerLevelCutOff (void) {
if (ActualPower > PowerCutOff){ //Check if Power Limit has been exceed
reading = 0;
encoderPosition = 0;
lcd.setCursor(0,3);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print("Exceeded Power");
lcd.setCursor(8,0);
lcd.print("OFF");
toggle = false; //switch Load Off
}
}
//----------------------Battery Constant Current Limit Value------------------------------------------
void batteryCurrentLimitValue (void) {
if (Mode == "BC" && reading > MaxBatteryCurrent){
reading = MaxBatteryCurrent;
encoderPosition = (MaxBatteryCurrent*1000); //keep encoder position value at 1000mA
}
}
//----------------------Display Rotary Encoder Input Reading on LCD---------------------------
void displayEncoderReading (void) {
lcd.setCursor(8,2); //start position of setting entry
if ( ( Mode == "CP" || Mode == "CR" ) && reading < 100 ) {
lcd.print("0");
}
if (reading < 10) { //add a leading zero to display if reading less than 10
lcd.print("0");
}
if ( Mode == "CP" || Mode == "CR" ) {
lcd.print (reading, 2); //show input reading from Rotary Encoder on LCD
} else {
lcd.print (reading, 3);
}
lcd.setCursor (CP, 2); //sets cursor position
lcd.cursor(); //show cursor on LCD
}
//--------------------------Cursor Position-------------------------------------------------------
//Change cursor the position routine
void CursorPosition(void) {
// Defaults for two digits before decimal and 3 after decimal point
int unitPosition = 9;
//Power and Resistance modes can be 3 digit before decimal but only 2 decimals
if ( Mode == "CP" || Mode == "CR" ) {
unitPosition = 10;
}
if (digitalRead(CursorPos) == LOW) {
delay(200); //simple key bounce delay
CP = CP + 1;
if (CP == unitPosition + 1 ) {
CP = CP + 1;
}
}
if (CP > 13) { CP = unitPosition; } //No point in turning tens and hundreds
if (CP == unitPosition +4 ) { factor = 1; }
if (CP == unitPosition +3 ) { factor = 10; }
if (CP == unitPosition +2 ) { factor = 100; }
if (CP == unitPosition ) { factor = 1000; }
}
//---------------------------------------------Read Voltage and Current--------------------------------------------------------------
void readVoltageCurrent (void) {
static int16_t result;
adc.configure(MCP342X_MODE_CONTINUOUS | MCP342X_CHANNEL_1 | MCP342X_SIZE_16BIT | MCP342X_GAIN_1X);
adc.startConversion();
adc.getResult(&result);
voltage = (long)result;
adc.configure(MCP342X_MODE_CONTINUOUS | MCP342X_CHANNEL_2 | MCP342X_SIZE_16BIT | MCP342X_GAIN_4X);
adc.startConversion();
adc.getResult(&result);
current = (long)result;
}
//-----------------------------------Calculate Actual Voltage and Current and display on LCD-----------------------------------------
void ActualReading(void) {
ActualCurrent = (((current*2.048)/32767) * 2.5); //calculate load current
currentDisplayCal(); //LCD display current calibration correction
ActualVoltage = (((voltage*2.048)/32767) * 50.4); //calculate load voltage upto 100v (was 50)
ActualPower = ActualVoltage*ActualCurrent;
if (ActualPower <=0){
ActualPower = 0;
}
if (ActualVoltage <=0.0){ //added to prevent negative readings on LCD due to error
ActualVoltage = 0.0;
}
if (ActualCurrent <= 0.0){ //added to prevent negative readings on LCD due to error
ActualCurrent = 0.0;
}
lcd.setCursor(0,1);
if ( ActualCurrent < 10.0 ) {
lcd.print(ActualCurrent,3);
} else {
lcd.print(ActualCurrent,2);
}
lcd.print("A");
lcd.print(" ");
if (ActualVoltage < 10.0) {
lcd.print(ActualVoltage, 3);
} else {
lcd.print(ActualVoltage, 2);
}
lcd.print("V");
lcd.print(" ");
if (ActualPower < 100 ) {
lcd.print(ActualPower,2);
} else {
lcd.print(ActualPower,1);
}
lcd.print("W");
lcd.print(" ");
}
//-----------------------DAC Control Voltage for Mosfet---------------------------------------
void dacControlVoltage (void) {
if (Mode == "CC"){
setCurrent = reading*1000; //set current is equal to input value in Amps
setReading = setCurrent; //show the set current reading being used
setControlCurrent = setCurrent * setCurrentCalibrationFactor;
controlVoltage = setControlCurrent;
}
if (Mode == "CP"){
setPower = reading*1000; //in Watts
setReading = setPower;
setCurrent = setPower/ActualVoltage;
setControlCurrent = setCurrent * setCurrentCalibrationFactor;
controlVoltage = setControlCurrent; //
}
if (Mode == "CR"){
setResistance = reading; //in ohms
setReading = setResistance;
setCurrent = (ActualVoltage)/setResistance*1000;
setControlCurrent = setCurrent * setCurrentCalibrationFactor;
controlVoltage = setControlCurrent;
}
if (Mode == "TC" || Mode == "TP" || Mode == "TT" || Mode == "TL"){ //Transient Modes
setControlCurrent = (setCurrent * 1000) * setCurrentCalibrationFactor;
controlVoltage = setControlCurrent;
}
}
//-------------------------------------Battery Capacity Discharge Routine----------------------------------------------------
void batteryCapacity (void) {
if (Mode == "BC"){
setCurrent = reading*1000; //set current is equal to input value in Amps
setReading = setCurrent; //show the set current reading being used
setControlCurrent = setCurrent * setCurrentCalibrationFactor;
controlVoltage = setControlCurrent;
lcd.setCursor(0,3);
lcd.print (timer.getTime()); //start clock and print clock time
Seconds = timer.getTotalSeconds(); //get totals seconds
LoadCurrent = ActualCurrent; //if timer still running use present Actual Current reading
if (timer.status() == 2){ //if timer is halted then use last Actual Current reading before timer stopped
LoadCurrent = BatteryCurrent;
}
BatteryLife = (LoadCurrent*1000)*(Seconds/3600); //calculate battery capacity in mAh
lcd.setCursor(9,3);
BatteryLife = round(BatteryLife);
if(BatteryLife >= BatteryLifePrevious){ //only update LCD (mAh) if BatteryLife has increased
if (BatteryLife < 10) { //add a 3 leading zero to display if reading less than 10
lcd.print("000");
}
if (BatteryLife >= 10 && BatteryLife <100){ //add a 2 leading zero to display
lcd.print("00");
}
if (BatteryLife >= 100 && BatteryLife <1000){ //add a 1 leading zero to display
lcd.print("0");
}
lcd.print(BatteryLife,0);
lcd.setCursor(13,3);
lcd.print("mAh");
BatteryLifePrevious = BatteryLife; //update displayed battery capacity on LCD
}
}
if (Mode == "BC" && ActualVoltage <= BatteryCutoffVolts){ //stops clock if battery reached cutoff level and switch load off
BatteryCurrent = ActualCurrent;
dac.setVoltage(0,false); //reset DAC to zero for no output current set at switch on
toggle = false; //Load is toggled OFF
lcd.setCursor(8,0);
lcd.print("OFF"); //indicate that LOAD is off at start up
timer.stop();
}
if (Mode == "BC" && Load == 1){ //Routine used for data logging in Battery Capacity Mode
if (Seconds != SecondsLog){ //only send serial data if time has changed
SecondsLog = Seconds;
Serial.print (SecondsLog); //sends serial data of time in seconds
Serial.print (","); //sends a comma as delimiter for logged data
Serial.println (ActualVoltage); //sends serial data of Voltage reading
}
}
}
//--------------------------------------------------Fan Control----------------------------------------------------------
void fanControl(void) {
temp = analogRead(temperature);
temp = temp * 0.107421875; // convert to Celsius
/*if (temp >= 40) { //if temperature 40 degree C or above turn fan on.
digitalWrite(fan, HIGH);
}
else {
digitalWrite(fan, LOW); //otherwise turn fan turned off
}*/
if (temp >= tempmax) {
digitalWrite(fan, HIGH);
}
else if (temp <= tempmin) {
digitalWrite(fan, LOW);
}
lcd.setCursor(16,0);
lcd.print(temp); //display temperature of heatsink on LCD
lcd.print((char)0xDF);
lcd.print("C");
}
//-----------------------Toggle Current Load ON or OFF------------------------------
void LoadSwitch(void) {
delay(200); //simple key bounce delay
if(toggle)
{
lcd.setCursor(8,0);
lcd.print("OFF");
current_instruction = 0; //reset current instruction for Transient List Mode to zero
last_time = 0; //reset last time to zero
transientPeriod = 0; //reset transient period time to zero
setCurrent = 0; //reset setCurrent to zero
toggle = !toggle;
Load = 0;
}
else
{
lcd.setCursor(8,0);
lcd.print("ON ");
lcd.setCursor(0,3);
lcd.print(" "); //clear bottom line of LCD
toggle = !toggle;
Load = 1;
}
}
//-----------------------Select Constant Current LCD set up--------------------------------
void Current(void) {
Mode = ("CC");
lcd.setCursor(0,0);
lcd.print("DC LOAD");
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print("Set I = ");
lcd.setCursor(16,2);
lcd.print(" ");
lcd.setCursor(14,2);
lcd.print("A");
lcd.setCursor(0,3); //clear last line of time info
lcd.print(" "); //20 spaces so as to allow for Load ON/OFF to still show
CP = 9; //sets cursor starting position to units.
}
//----------------------Select Constant Power LCD set up------------------------------------
void Power(void) {
Mode = ("CP");
lcd.setCursor(0,0);
lcd.print("DC LOAD");
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print("Set W = ");
lcd.setCursor(16,2);
lcd.print(" ");
lcd.setCursor(14,2);
lcd.print("W");
lcd.setCursor(0,3); //clear last line of time info
lcd.print(" "); //20 spaces so as to allow for Load ON/OFF to still show
CP = 10; //sets cursor starting position to units.
}
//----------------------- Select Constant Resistance LCD set up---------------------------------------
void Resistance(void) {
Mode = ("CR");
lcd.setCursor(0,0);
lcd.print("DC LOAD");
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print("Set R = ");
lcd.setCursor(16,2);
lcd.print(" ");
lcd.setCursor(14,2);
lcd.print((char)0xF4);
lcd.setCursor(0,3); //clear last line of time info
lcd.print(" "); //20 spaces so as to allow for Load ON/OFF to still show
CP = 10; //sets cursor starting position to units.
}
//----------------------- Select Battery Capacity Testing LCD set up---------------------------------------
void BatteryCapacity(void) {
Mode = ("BC");
lcd.setCursor(0,0);
lcd.print("BATTERY");
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,2);
lcd.print("Set I = ");
lcd.setCursor(14,2);
lcd.print("A");
lcd.setCursor(0,3); //clear last line of time info
lcd.print(" "); //20 spaces so as to allow for Load ON/OFF to still show
}
//----------------------Battery Type Selection Routine------------------------------------------------
void batteryType (void) {
exitMode = 0; //reset EXIT mode
lcd.noCursor(); //switch Cursor OFF for this menu
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Select Battery Type");
lcd.setCursor(0,1);
lcd.print("1=LiPo/Li-Ion 2=LiFe");
lcd.setCursor(0,2);
lcd.print("3=NiCd/NiMH 4=ZiZn");
lcd.setCursor(0,3); //clear last line of time info
lcd.print("5=Set Voltage 6=Exit"); //20 spaces so as to allow for Load ON/OFF to still show
customKey = customKeypad.waitForKey(); //stop everything till the user press a key.
if (customKey == '1'){
BatteryCutoffVolts = LiPoCutOffVoltage;
BatteryType = ("LiPo");
}
if (customKey == '2'){
BatteryCutoffVolts = LiFeCutOffVoltage;
BatteryType = ("LiFe");
}
if (customKey == '3'){
BatteryCutoffVolts = NiCdCutOffVoltage;
BatteryType = ("NiCd");
}
if (customKey == '4'){
BatteryCutoffVolts = ZiZnCutOffVoltage;
BatteryType = ("ZiZn");
}
if (customKey == '5'){
BatteryType = ("SetV");
}
if (customKey == '6'){ //Exit selection screen
exitMode = 1;
}
if (customKey == '7' || customKey == '8' || customKey == '9' || customKey == '0' || customKey == 'A' || customKey == 'B' || customKey == 'C' || customKey == 'D' || customKey == '*' || customKey == '#' || customKey == 'E' || customKey == 'F' || customKey == '<' || customKey == '>' ){
batteryType(); //ignore other keys
}
if (BatteryType == "LiPo" && exitMode != 1) {
setBatteryCells();
BatteryCutoffVolts = (BatteryNumOfCells*LiPoCutOffVoltage);
//BatteryType = BatteryType+" "+String(BatteryNumOfCells)+"cells";
}
if (BatteryType == "LiFe" && exitMode != 1) {
setBatteryCells();
BatteryCutoffVolts = (BatteryNumOfCells*LiFeCutOffVoltage);
}
if (BatteryType == "NiCd" && exitMode != 1) {
setBatteryCells();
BatteryCutoffVolts = (BatteryNumOfCells*NiCdCutOffVoltage);
}
if (BatteryType == "ZiZn" && exitMode != 1) {
setBatteryCells();
BatteryCutoffVolts = (BatteryNumOfCells*ZiZnCutOffVoltage);
}
if(BatteryType == "SetV" && exitMode != 1){
setBatteryCutOff();
}
batteryTypeSelected(); //briefly display battery type selected and discharge cut off voltage
lcd.clear();
}
//--------------------------Set DAC Voltage--------------------------------------------
void dacControl (void) {
if (!toggle){
dac.setVoltage(0,false); //set DAC output voltage to 0 if Load Off selected
if(Mode == "BC" && ActualVoltage >= BatteryCutoffVolts && timer.status() == 1){
timer.stop();
}
}else{
//Serial.println("Control Voltage"); //used for testing only
//Serial.println(controlVoltage); //used for testing only
dac.setVoltage(controlVoltage,false); //set DAC output voltage for Range selected
if(Mode == "BC" && ActualVoltage >= BatteryCutoffVolts && timer.status() != 1){
timer.start();
}
}
}
//--------------------------Battery Selected Information--------------------------------------------
void batteryTypeSelected (void) {
if (exitMode !=1){ //if battery selection was EXIT then skip this routine
lcd.clear();
lcd.setCursor(2,0);
lcd.print("Battery Selected");
lcd.setCursor(8,1);
lcd.print(BatteryType); //display battery type selected
lcd.setCursor(2,2);
lcd.print("Discharge Cutoff");
lcd.setCursor(6,3);
lcd.print(BatteryCutoffVolts); //display battery discharge cut off voltage
lcd.print(" volts");
delay(3000);
}
}
//--------------------------Set Battery Cut-Off Voltage--------------------------------------------
void setBatteryCutOff (void) {
lcd.clear();
lcd.setCursor(4,0);
lcd.print("Enter Battery");
lcd.setCursor(3,1);
lcd.print("Cut-Off Voltage");
y = 8;
z = 8;
r = 2;
inputValue();
BatteryCutoffVolts = x;
lcd.clear();
}
//--------------------------Set Battery number of Cells--------------------------------------------
void setBatteryCells(void) {
lcd.clear();
lcd.setCursor(3, 0);
lcd.print("Enter Number of");
lcd.setCursor(3, 1);
lcd.print("Cells in Series");
y = 8;
z = 8;
r = 2;
inputValue();
//BatteryNumOfCells = x;
BatteryNumOfCells = 1;
lcd.clear();
}
//------------------------Key input used for Battery Cut-Off and Transient Mode------------------------
void inputValue (void){
while(customKey != 'F'){ //check if enter pressed (was previously #)
customKey = customKeypad.getKey();
if(customKey >= '0' && customKey <= '9'){ //check for keypad number input
numbers[index++] = customKey;
numbers[index] = '\0';
lcd.setCursor(z,r);
lcd.print(customKey); //show number input on LCD
z = z+1;
}
if(customKey == '>'){ //Decimal point
if (decimalPoint != ('>')){ //test if decimal point entered twice - if so ski
numbers[index++] = '.';
numbers[index] = '\0';
lcd.setCursor(z,r);
lcd.print(".");
z = z+1;
decimalPoint = ('>'); //used to indicate decimal point has been input
}
}
if(customKey == '<'){ //clear entry
index = 0;
z = y;
lcd.setCursor(y,r);
lcd.print(" ");
numbers[index] = '\0'; //
decimalPoint = (' '); //clear decimal point test character reset
}
}
if(customKey == 'F') { //enter value
x = atof(numbers);
index = 0;