-
Notifications
You must be signed in to change notification settings - Fork 1
/
SimpleArduinoBatteryCapacity.ino
297 lines (274 loc) · 11.4 KB
/
SimpleArduinoBatteryCapacity.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
/*
@File : SimpleArduinoBatteryCapacity.ino
@Time : 2023/01/09
@Author : joyel24
@Contributors : joyel24
*/
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#include "OneButton.h" //http://www.mathertel.de/Arduino/OneButtonLibrary.aspx
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUM_OF_AVERAGE_SAMPLES 800 //Number of values picked for average adc calculation
#define MEASURED_VCC_REF 4.966 //Measured voltage accross ground and Vcc pin of your arduino (it can depends on its supply)
#define MEASURED_VCC_WITH_LOAD 4.966 //As using relay consume current, you need to check VCC voltage drop when relay is on to stay relative for ADC convertion as it's critical for internal resistance calculation (you may have negative values)
#define SHUNT_RESISTOR_OHM 1.00 //Value of Shunt Resistor (Ohm) to measure intensity of discharge
#define PIN_RELAY_OR_MOSFET 7 //PIN used to drive the relay to start or stop the discharge
#define PIN_BUTTON 5 //Pin of the button to start/stop discharge
#define STOP_DISCHARGE_VOLTAGE 0.85 //Stop discharge protection !!!!!!!!!!!!To Improove!!!!!!!!!!!
#define VOLTAGE_PIN A3 //Battery voltage pin input
#define INTENSITY_PIN A0 //Pin connected to Shunt Resistor
#define PIN_BUTTON_FOR_MENU 16 //Pin connected to Menu/Function button
#define NUM_ATTEMPTS_INTERNAL_RES 5 //Number of attempts to calculate internal resistance
#define INTERNAL_RESITANCE_DURATION 100 //Duration of each internal resistance test
#define MEASURED_LOAD_RESISTANCE 17.175 //Measured load resistance (I recommend using OhmMeter in the battery socket with discharge ON with #define STOP_DISCHARGE_VOLTAGE 0.00)
#define ADC_INTENSITY_OFFSET_MINIMUM (13.20-4.8387)
float mAsEEPROMstored; //Variable synced to EEPROM mas data (mAs=mah*3600)
float mAs = 0; //milli-Ampere-seconds : mAh=mAs/3600
uint32_t secondsElapsed;
bool menuActive = false;
bool exitMenu = false;
OneButton button(PIN_BUTTON_FOR_MENU, true); //button definition for OneButton Library http://www.mathertel.de/Arduino/OneButtonLibrary.aspx
int selectedMenu; //Variable corresponding to the active item in menu
uint32_t antiMenuLoopbackMilliSeconds; //Anti Loopback after menu exited
void setup() {
pinMode(PIN_BUTTON, INPUT);
pinMode(PIN_RELAY_OR_MOSFET, OUTPUT);
digitalWrite(PIN_RELAY_OR_MOSFET, LOW); //Ensure discharge relay is off
pinMode(PIN_BUTTON_FOR_MENU, INPUT_PULLUP); //
button.attachLongPressStart(menulongPress); //Theses lines are needed for the OneButton Library
button.attachClick(oneClick); //Run function name beetween () when button action is detected
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
display.clearDisplay();
// Display Welcome Message during 2sec
display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Welcome");
display.display();
delay(2000);
}
void menulongPress(){
if (menuActive == false && (millis() - antiMenuLoopbackMilliSeconds > 2000) ){
Menu();
}
switch (selectedMenu){
case 1:
mAsEEPROMstored = 0;
EEPROM.put(0, mAsEEPROMstored);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("EEPROM data set to 0");
display.display();
delay(1500);
break;
case 2:
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 0);
display.print(getInternalRes(MEASURED_LOAD_RESISTANCE, NUM_ATTEMPTS_INTERNAL_RES, INTERNAL_RESITANCE_DURATION), 0);
display.print("mOhm");
display.display();
delay(5000);
break;
case 3:
exitMenu = true;
menuActive = false;
antiMenuLoopbackMilliSeconds = millis();
break;
}
}
void oneClick(){
if (menuActive == false && (millis() - antiMenuLoopbackMilliSeconds > 2000) ){
Menu();
}
else if (menuActive == true){
switch (digitalRead(PIN_BUTTON)){
case LOW:
selectedMenu+=1;
break;
case HIGH:
selectedMenu-=1;
break;
}
}
}
//Function to get adc value from a desired pin with averaging samples defined by NUM_OF_AVERAGE_SAMPLES
int getadcValue(int pin, int numSamples){
uint32_t adcValuesTotal = 0;
for (int n=0; n<numSamples; n++){ //Averaging values for n sample
adcValuesTotal += analogRead(pin);
}
return adcValuesTotal / numSamples;;
}
float getVoltage(int pin){
float value = getadcValue(pin, NUM_OF_AVERAGE_SAMPLES);
if (digitalRead(PIN_RELAY_OR_MOSFET) == LOW){
return value / 1023 * MEASURED_VCC_REF;
}
else {return value / 1023 * MEASURED_VCC_WITH_LOAD;}
}
float getIntensity(int pin){
float voltage = getVoltage(pin);
if (voltage == 0){
return voltage / SHUNT_RESISTOR_OHM;
}
else return (-10/1000+(voltage / SHUNT_RESISTOR_OHM)+(ADC_INTENSITY_OFFSET_MINIMUM/1000));
}
float getPower(){
float voltage = getVoltage(VOLTAGE_PIN);
float intensity = getIntensity(INTENSITY_PIN);
float power = (voltage * intensity);
return power;
}
float getInternalRes(float loadRes, int attempts, int msDelay){
display.clearDisplay();
float accumulatedInternalResForAverage=0;
for (int n=0; n<attempts; n++){
float CalculatedInternalRes=0;
float deltaVoltage=0;
float ratioVoltage=0;
float initialVoltage = getVoltage(VOLTAGE_PIN);
uint32_t StartMillis = millis();
digitalWrite(PIN_RELAY_OR_MOSFET, HIGH);
while (millis() - StartMillis <= msDelay){
}
float droppedVoltage = getVoltage(VOLTAGE_PIN);
deltaVoltage = initialVoltage - droppedVoltage;
ratioVoltage = droppedVoltage/initialVoltage;
CalculatedInternalRes = 1000*(-MEASURED_LOAD_RESISTANCE+MEASURED_LOAD_RESISTANCE*(1/ratioVoltage));
accumulatedInternalResForAverage += CalculatedInternalRes;
/*Serial.print(initialVoltage, 3);Serial.println("V init");
Serial.print(n);Serial.print("n ");Serial.println(droppedVoltage, 3);
Serial.print("deltaVoltage : ");Serial.println(deltaVoltage, 3);
Serial.print("ratioVoltage");Serial.println(ratioVoltage, 3);
Serial.print("CalculatedInternalRes : ");Serial.println(CalculatedInternalRes);
Serial.print("DeltaVMoyenne : ");Serial.println((accumulatedDeltaVoltageForAverage / (n+1)), 3);Serial.println();
*/
digitalWrite(PIN_RELAY_OR_MOSFET, LOW);
delay(msDelay);
//Serial.print("CalculatedInternalRes : ");Serial.println(CalculatedInternalRes);
//Serial.print("Moy : ");Serial.println(accumulatedInternalResForAverage / (n+1));
}
//Serial.println(accumulatedInternalResForAverage / attempts);
return accumulatedInternalResForAverage / attempts;
}
void displayRefresh(uint32_t SecondsElapsed){ //Function to display instant voltage, intensity and power on 1st line & other function parameters
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print(getVoltage(VOLTAGE_PIN), 3); display.print("V "); //Display Voltage
display.print(1000* (getIntensity(INTENSITY_PIN)), 0); display.print("mA "); //Display Intensity
display.print(getPower()); display.print("W "); //Display Power
display.setCursor(0, 9);
display.print(SecondsElapsed);; display.print("s "); //Timer
display.print(mAs/3600); display.println("mAh ");
EEPROM.get(0, mAsEEPROMstored); //get float data stored from address 0 of EEPROM (Address 0 to 3 as float is 4bytes long)
display.print(mAsEEPROMstored / 3600);display.println("mAh ");
display.display();
}
void Menu(){
menuActive = true;
exitMenu = false;
display.clearDisplay();
display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("MENU");
display.display();
delay(500);
while (exitMenu == false){
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
switch (selectedMenu){
case 1:
display.println(">Reset EEPROM");
display.println("Internal Resistance");
display.println("Exit Menu");
break;
case 2:
display.println("Reset EEPROM");
display.println(">Internal Resistance");
display.println("Exit Menu");
break;
case 3:
display.println("Reset EEPROM");
display.println("Internal Resistance");
display.println(">Exit Menu");
break;
default:
if (selectedMenu < 1){
selectedMenu = 3;
break;
}
else {selectedMenu = 1;}
break;
}
button.tick();
display.display();
}
}
void loop() {
uint32_t xxx=0;
uint32_t yyy=0;
bool STOP=false;
digitalWrite(PIN_RELAY_OR_MOSFET, LOW);
int millisecFormAsInterval = 2000;
float mAsTempStoredForEEPROMdelay=0;
displayRefresh(secondsElapsed);
button.tick();
while (digitalRead(PIN_BUTTON) == HIGH){
mAsTempStoredForEEPROMdelay = mAsEEPROMstored;
uint32_t resetTime = millis();
uint32_t startMillisFormAs = millis();
mAs = 0; //Reset mA of actual counter
if (STOP==false){
while (STOP==false && (getVoltage(VOLTAGE_PIN) >= STOP_DISCHARGE_VOLTAGE)){
if (digitalRead(PIN_BUTTON) == LOW){
break;
}
digitalWrite(PIN_RELAY_OR_MOSFET, HIGH);
uint32_t TimeElapsed = millis() - resetTime;
//if ( (millis()-startMillisFormAs) >= millisecFormAsInterval){
if ( (TimeElapsed/millisecFormAsInterval) > xxx/(millisecFormAsInterval/1000) ) {
mAs += (getIntensity(INTENSITY_PIN)*1000) * (millisecFormAsInterval/1000);
mAsEEPROMstored += (getIntensity(INTENSITY_PIN)*1000) * (millisecFormAsInterval/1000);
mAsTempStoredForEEPROMdelay += (getIntensity(INTENSITY_PIN)*1000) * (millisecFormAsInterval/1000);
xxx+=2;
//Serial.print(yyy);Serial.print(" ");Serial.print(TimeElapsed/1000);Serial.print(" ");Serial.println(xxx);
if ( (EEPROM.read(0)!=mAsEEPROMstored) && ((TimeElapsed/1000)>(yyy*2)) ){
//EEPROM.put(0, mAsEEPROMstored);
EEPROM.put(0, mAsTempStoredForEEPROMdelay);
//display.setTextSize(1);
//display.setTextColor(WHITE);
//display.setCursor(64, 20);
yyy+=5;
//display.print(xxx);
//display.setCursor(84, 20);
//display.print(millis()-startMillisFormAs);
//display.display();
}
startMillisFormAs = millis();
}
displayRefresh(TimeElapsed/1000);
secondsElapsed = TimeElapsed/1000;
}
digitalWrite(PIN_RELAY_OR_MOSFET, LOW);
STOP=true;
}
EEPROM.put(0, mAsTempStoredForEEPROMdelay);
}
}