-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbatBalancerSW.h
170 lines (156 loc) · 4.37 KB
/
batBalancerSW.h
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
#ifndef _BAT_BALLANCERSW_H_
#define _BAT_BALLANCERSW_H_
/*
*
* Ugly hack to switch an active balancer on/off
*
* keeping 4 pin control to reuse hardware
*
*/
#include "bat.h"
// if data is older than this, consider battery data stale and unreliable
#define BAL_STALE 15000UL
// minimum charge current (+ve for charge) before balancing starts
#define BAL_MIN_CURRENT 0.0f
// minimum voltage for a single cell before balancing starts
#define BAL_START_VOLTAGE 3.5f
// minimum voltage for a single cell before balancing stops
#define BAL_STOP_VOLTAGE 3.45f
class balUnit {
public:
balUnit(batBat * bat_, int pin_) :
bat(bat_),
pin(pin_),
active(false)
{}
private:
batBat * bat;
int pin;
bool active;
friend class balBank;
};
class balBank {
public:
balBank(balUnit * units_, int num, bool invert_) :
units(units_),
numUnits(num),
invert(invert_),
lastTime(0)
{}
// call once in setup
// initialise balancer and pins
// call early in setup to make sure balance relays are off!
void init(void) {
for(int i = 0; i < numUnits; i++) {
pinMode(units[i].pin, OUTPUT);
pinOff(i);
}
// cycle relays for test/verification
for(int i = 0; i < numUnits; i++) {
pinOn(i);
}
delay(1000);
for(int i = 0; i < numUnits; i++) {
pinOff(i);
units[i].active = false;
}
// relay test
//esp_restart();
//while(1) {}
}
// arduino loop style function - call repeatedly, and this handles all logic internally
void run(void) {
// first scan all units for updates and compare to our last time
// just use the sum, as even if it wraps, it should give a unique state for all batteries...
unsigned long testms = 0;
for(int i = 0; i < numUnits; i++) testms += units[i].bat->updateMillis;
if(testms == lastTime) return;
lastTime = testms;
Serial.println("BALANCER RUN!");
unsigned long oldest = 0;
float max_voltage = 0.0f;
float min_current = 999.0f;
for(int i = 0; i < numUnits; i++) {
if(units[i].bat->updateMillis) {
unsigned long age = millis() - units[i].bat->updateMillis;
if(age > oldest) oldest = age;
} else {
oldest = BAL_STALE * 2; // force stale, if not yet updated
}
if(units[i].bat->current < min_current) min_current = units[i].bat->current;
if(units[i].bat->maxCellVoltage > max_voltage) max_voltage = units[i].bat->maxCellVoltage;
}
Serial.print("Age: ");
Serial.println(oldest);
Serial.print("Max V: ");
Serial.println(max_voltage);
Serial.print("min I: ");
Serial.println(min_current);
// 1 - check for stale data, and pause balancing if data is too old
if(oldest > BAL_STALE) {
Serial.println("DATA STALE - STOPPING BALANCER!");
stopAll();
return;
}
// 2 - check for start conditions
// min current
if(-min_current < BAL_MIN_CURRENT) {
Serial.println("CURRENT TOO LOW - STOPPING BALANCER!");
stopAll();
return;
}
// all active states are the same - just check first
if(units[0].active) {
// active - evaluate stop voltage
if(max_voltage < BAL_STOP_VOLTAGE) {
Serial.println("VOLTAGE TOO LOW - STOPPING BALANCER!");
stopAll();
}
} else {
// not active - evaluate start voltage
if(max_voltage > BAL_START_VOLTAGE) {
Serial.println("VOLTAGE HIGH - START BALANCER!");
startAll();
}
}
}
private:
// turn a pin on
void pinOn(int unit) {
// as a sanity check, and to allow calling from init, always write the pin, even if we think state is already correct.
if(invert) {
digitalWrite(units[unit].pin, LOW);
} else {
digitalWrite(units[unit].pin, HIGH);
}
if(units[unit].active) return;
units[unit].active = true;
}
// turn a pin off
void pinOff(int unit) {
if(invert) {
digitalWrite(units[unit].pin, HIGH);
} else {
digitalWrite(units[unit].pin, LOW);
}
if(!units[unit].active) return;
units[unit].active = false;
}
// turn off all pins and return to inactive state
void stopAll(void) {
for(int i = 0; i < numUnits; i++) {
pinOff(i);
}
}
// turn on all pins and aet active state
void startAll(void) {
for(int i = 0; i < numUnits; i++) {
pinOn(i);
}
}
balUnit * units;
int numUnits;
bool invert;
unsigned long lastTime;
};
#endif