1
0
mirror of https://github.com/sjlongland/cluster-powerctl.git synced 2025-09-13 12:03:14 +10:00

powerctl: Re-work controller logic.

This commit is contained in:
Stuart Longland 2018-09-01 22:24:39 +10:00
parent dd0f371b7c
commit 03161f8d8e
Signed by: stuartl
GPG Key ID: 6AA32EFB18079BAA
4 changed files with 213 additions and 362 deletions

118
README.md
View File

@ -1,11 +1,12 @@
Power controller for solar powered personal cloud
=================================================
This firmware is intended to control the charging of a battery bank that
powers a small computer cluster running a private cloud system. The
idea is to try to keep the battery within a small range of voltages,
charging from solar or mains based chargers as necessary to top the
battery back up.
This firmware is intended to control the charging of a battery bank that powers
a small computer cluster running a private cloud system. The system has two
charging sources: mains power and solar power.
The chargers are assumed to have a logic-level signal which, when pulled low,
shuts down the relevant charger.
Circuit description
-------------------
@ -13,20 +14,22 @@ Circuit description
An ATTiny24A microcontroller runs from a 5V rail derived from the
battery. It uses its internal RC oscillator running at 1MHz.
Connected to PB0 and PB1 are the two MOSFETs that turn on the input
sources, one for solar (PB1), the other for a mains charger (PB0).
Connected to PB0 and PB1 are the two transistors that turn on the input
sources, one for solar (PB1), the other for a mains charger (PB0). These are
NPN bi-polar junction transistors. When the base is turned on via PB0 or PB1,
this allows current to flow from the collector to ground, effectively pulling
the relevant logic level output low.
The source pins of these connect to the battery positive input, so when
the FET is on, power may flow from that source to the battery.
A third MOSFET connects to PB2, this is PWM-controlled to manage some
cooling fans. The internal temperature sensor is used to decide whether
the fans should be on or off, and at what speed.
Connected to PB2 is a third NPN transistor which is wired to a P-channel
MOSFET, such that turning the transistor on also turns on that MOSFET. This
is PWM-controlled to manage some cooling fans. The internal temperature
sensor is used to decide whether the fans should be on or off, and at what
speed.
Connecting to each input, and to the battery, are separate voltage
dividers, comprising of a 1.5kOhm and 100Ohm resistors. These divide
the input voltage by 16, and the divided voltage is fed into the ADC
pins PA0 (mains), PA1 (solar) and PA2 (battery).
pins PA1 (solar) and PA2 (battery).
LEDs connect to PA7, PA6, PA5, PA4 and PA3 to 0v. ICSP is shared with the
LEDs.
@ -34,18 +37,17 @@ LEDs.
GPIOs
-----
PB0: Mains MOSFET (active HIGH)
PB1: Solar MOSFET (active HIGH)
PB0: Mains enable (active LOW)
PB1: Solar enable (active LOW)
PB2: Fan MOSFET (active HIGH)
PB3: nRESET
PA7: Temperature Low LED (active HIGH)
PA6: Temperature High LED (active HIGH) + ICSP MOSI
PA5: Battery Voltage High LED (active HIGH) + ICSP MISO
PA4: Warning LED (active high) + ICSP SCK + Debug Tx
PA5: Mains Floating LED (active HIGH) + ICSP MISO
PA4: Mains Charging LED (active high) + ICSP SCK + Debug Tx
PA3: Battery Voltage Good LED (active HIGH)
PA2: Analogue Input: Battery voltage
PA1: Analogue Input: Solar voltage
PA0: Analogue Input: Mains voltage
Firmware description
--------------------
@ -64,48 +66,60 @@ loop. If DEBUG is enabled, "INIT xx" will be displayed, where xx is the
value of the MCUSR register.
Two counters are decremented by the Timer 1 overflow interrupt.
`led_timeout` causes the main loop to update the state of the LEDs when
it hits zero, and `adc_timeout` triggers a new ADC capture run when it
reaches zero.
`t_second` causes the main loop to decrement each one-second timer, `t_adc`
causes the ADC state machine to advance one tick and checkt the state of the
channels.
The three analogue inputs and temperature sensor are scanned when
`adc_timeout` reaches zero, then the state analysed. The battery
voltage is compared to the previous reading to dissern if the battery is
charging, discharging or holding steady.
The two analogue inputs and temperature sensor are scanned when
`t_adc` reaches zero, then the state analysed.
If this state has not changed, a battery state counter increments,
otherwise it is reset.
The charger is in one of four states:
* initialisation
* solar
* charging from mains
* floating on mains
The current state of the FETs is checked. Three states are valid:
- Idle state: all FETs off
- Mains charge: MAINS FET turned on
- Solar charge: SOLAR FET turned on
On power up, we enter the initialisation state. In this state, we wait for
the first ADC readings to arrive before deciding on whether we run from mains
power or solar. During this time, all power inputs are inhibited.
IF statements at this point compare the battery voltage to the
thresholds, and decide whether to switch voltage or not.
If either the battery or solar input are below minimum thresholds, the mains
charger is turned on and we enter the "charging from mains" state.
Otherwise the solar input is used and we enter the "solar" state.
In the "solar" state, we monitor the battery voltage. If it drops below the
minimum voltage, we switch to the "charging from mains" state.
In the "charging from mains" state, we monitor the battery charging progress.
Upon reaching the high voltage threshold, we switch to the "floating on mains"
state.
In the "floating on mains" state, we wait a minimum of one hour for the
mains charger to finish its cycle. If the battery drops below the
high-voltage threshold, we move back to the "charging from mains" state.
Once an hour has elapsed in the floating state, if the solar input is above
the minimum threshold, we turn off the mains charger and switch to the "solar"
state.
LED indications
---------------
The LEDs have the following meanings:
Warning LED (PA4; DEBUG undefined):
- Off: No warning condition
- Flashing: Battery below critical threshold or temperature above
maximum threshold
- On: Not used
When in DEBUG mode, this LED may flicker with serial activity, and will
remain ON when idle.
Temperature LEDs (PA6, PA7):
- PA7 On, PA6 Off: Temperature below minimum threshold
- PA7 Flashing, PA6 Off: Temperature above minimum threshold
- PA7 Off, PA6 Flashing: Temperature above maximum threshold
- Other states: not used
- PA7 Off, PA6 Off: Temperature below minimum threshold
- PA7 On, PA6 Off: Temperature between thresholds
- PA7 Off, PA6 On: Temperature above maximum threshold
Battery LEDs (PA5, PA3):
- PA3 Flashing, PA5 Off: Battery below low threshold
- PA3 Off, PA5 Flashing: Battery above high threshold
- PA3 On, PA5 Off: Battery is in "good" range (between low and high)
- Other states: not used
Battery State LED (PA3):
- PA3 Off: Battery is below minimum voltage
- PA3 On: Battery is above minimum voltage
Mains Charger State LEDs (PA4, PA5):
- PA4 Off, PA5 Off: Mains supply is off
- PA4 On, PA5 Off: Mains supply is charging the battery
- PA4 Off, PA5 On: Mains supply is floating the battery
Note that in debug mode, PA4 instead becomes the serial TX line, and so will
remain on in the idle state, and will flicker with serial activity.

View File

@ -22,15 +22,15 @@
/* LEDs */
#define LED_TEMP_LOW (1 << 7)
#define LED_TEMP_HIGH (1 << 6)
#define LED_BATT_HIGH (1 << 5)
#define LED_WARNING (1 << 4)
#define LED_BATT_FLT (1 << 5)
#define LED_BATT_CHG (1 << 4)
#define LED_BATT_GOOD (1 << 3)
#define LED_PORT PORTA
#define LED_PORT_DDR_REG DDRA
#define LED_PORT_DDR_VAL ( LED_TEMP_LOW \
| LED_TEMP_HIGH \
| LED_BATT_HIGH \
| LED_WARNING \
| LED_BATT_FLT \
| LED_BATT_CHG \
| LED_BATT_GOOD )
/* MOSFETs */

View File

@ -38,36 +38,22 @@
)
/* --- Thresholds --- */
#define V_CH_ADC ADC_READ(V_CH_MV)
#define V_H_ADC ADC_READ(V_H_MV)
#define V_L_ADC ADC_READ(V_L_MV)
#define V_CL_ADC ADC_READ(V_CL_MV)
#define V_DELTA_ADC ADC_READ(V_DELTA_MV)
#define V_SOL_MIN_ADC ADC_READ(V_SOL_MIN_MV)
/* --- Timeouts --- */
#define T_LED_TICKS TIMER_TICKS(T_LED_MS)
#define T_ADC_TICKS TIMER_TICKS(T_ADC_MS)
#define STATE_DIS_CHECK (0) /*!< Check voltage in discharge state */
#define STATE_DIS_WAIT (1) /*!< Wait in discharge state */
#define STATE_CHG_CHECK (2) /*!< Check voltage in charging state */
#define STATE_CHG_WAIT (3) /*!< Wait in charging state */
#define STATE_INIT (0) /*!< Initial start-up state */
#define STATE_SOLAR (1) /*!< Running from solar */
#define STATE_MAINS_CHG (2) /*!< Charging from mains */
#define STATE_MAINS_FLT (3) /*!< Floating on mains */
/*!
* Charger state machine state. We have four states we can be in.
*/
static volatile uint8_t charger_state = STATE_DIS_CHECK;
#define SRC_NONE (0) /*!< Turn off all chargers */
#define SRC_SOLAR (1) /*!< Turn on solar charger */
#define SRC_MAINS (2) /*!< Turn on mains charger */
#define SRC_ALT (3) /*!< Alternate to *other* source,
* valid for select_src only.
*/
/*!
* Charging source.
*/
static volatile uint8_t charge_source = SRC_NONE;
static volatile uint8_t charger_state = STATE_INIT;
/*!
* For state machine, the last state of the ADC MUX so we know whether
@ -76,16 +62,15 @@ static volatile uint8_t charge_source = SRC_NONE;
*/
static volatile uint8_t last_admux = 0;
/*!
* For state machine, determines what the battery was one sample ago so
* we know if it's charging, discharging, or remaining static. ADC units.
*/
static volatile uint16_t v_bl_adc = 0;
/*!
* Current reading of the battery voltage in ADC units.
*/
static volatile uint16_t v_bn_adc = 0;
static volatile uint16_t v_bat_adc = 0;
/*!
* Current reading of the solar voltage in ADC units.
*/
static volatile uint16_t v_sol_adc = 0;
/*!
* Current reading of the internal temperature sensor in ADC units.
@ -103,9 +88,9 @@ static volatile uint16_t t_second = 0;
static volatile uint16_t t_adc = 0;
/*!
* How long before we change LED states?
* Float timeout
*/
static volatile uint16_t t_led = 0;
static volatile uint16_t t_float = 0;
/*!
* Fan kick-start timeout
@ -113,19 +98,9 @@ static volatile uint16_t t_led = 0;
static volatile uint8_t t_fan = 0;
/*!
* Charger timeout
* ADC readings taken?
*/
static volatile uint8_t t_charger = T_LF_S;
/*!
* Charger warning timeout
*/
static volatile uint8_t t_cwarn = 0;
/*!
* Are we presently in a warning state?
*/
static volatile uint8_t charger_warning = 0;
static volatile uint8_t adc_checked = 0;
/* Debug messages */
#ifdef DEBUG
@ -162,190 +137,99 @@ static inline void uart_tx_bool(const char* msg, uint8_t val) {
#endif
/*!
* Enter charger warning state. This indicates that the battery
* *should* be charging, but isn't due to insufficient input current from
* the charger.
* Switch to charging from mains power.
*/
static inline void enter_warning() {
if (charger_warning)
LED_PORT |= LED_WARNING;
else
charger_warning = 1;
static void enter_mains_chg(void) {
/* Enable mains power */
FET_PORT &= ~FET_MAINS;
/* Reset our timer */
t_cwarn = T_CWARN_S;
/* Indicate via LEDs */
LED_PORT |= LED_BATT_CHG;
LED_PORT &= ~LED_BATT_FLT;
/* Enter state */
charger_state = STATE_MAINS_CHG;
}
/*!
* Leave the charger warning state. This indicates the charger has left
* the charging state or the battery has begun charging.
* Switch to floating on mains power.
*/
static inline void exit_warning() {
charger_warning = 0;
t_cwarn = 0;
LED_PORT &= ~LED_WARNING;
static void enter_mains_float(void) {
/* Reset timer */
t_float = T_FLOAT_S;
/* Indicate via LEDs */
LED_PORT &= ~LED_BATT_CHG;
LED_PORT |= LED_BATT_FLT;
/* Enter state */
charger_state = STATE_MAINS_FLT;
}
/*!
* Switch between chargers. This is does a "break-before-make" switchover
* of charging sources to switch from mains to solar, solar to mains, or to
* switch from charging to discharging mode. It expressly forbids turning
* both chargers on simultaneously.
*
* Added is the ability to just alternate between sources.
* Switch to running on solar.
*/
static void select_src(uint8_t src) {
if (src == SRC_ALT) {
if (charge_source == SRC_SOLAR)
src = SRC_MAINS;
else
src = SRC_SOLAR;
}
#ifdef DEBUG
uart_tx_str(STR_SELECT_SRC);
#endif
switch(src) {
case SRC_SOLAR:
FET_PORT &= ~FET_MAINS;
FET_PORT |= FET_SOLAR;
charge_source = SRC_SOLAR;
#ifdef DEBUG
uart_tx_str(STR_SRC_SOLAR);
#endif
break;
case SRC_MAINS:
FET_PORT &= ~FET_SOLAR;
FET_PORT |= FET_MAINS;
charge_source = SRC_MAINS;
#ifdef DEBUG
uart_tx_str(STR_SRC_MAINS);
#endif
break;
case SRC_NONE:
default:
FET_PORT &= ~FET_SRC_MASK;
charge_source = SRC_NONE;
#ifdef DEBUG
uart_tx_str(STR_SRC_NONE);
#endif
break;
}
#ifdef DEBUG
uart_tx_str(STR_NL);
#endif
static void enter_solar(void) {
/* Inhibit mains */
FET_PORT |= FET_MAINS;
/* Indicate via LEDs */
LED_PORT &= ~(LED_BATT_FLT | LED_BATT_CHG);
/* Enter state */
charger_state = STATE_SOLAR;
}
static void discharge_check() {
/* Decide when we should do our next check */
#ifdef DEBUG
uart_tx_str(STR_DIS); uart_tx_str(STR_CHK);
uart_tx_bool(STR_V_BN_GE_V_H, v_bn_adc >= V_H_ADC);
#endif
if (v_bn_adc >= V_H_ADC)
t_charger = T_LF_S;
else
t_charger = T_HF_S;
/*!
* Checks at start-up
*/
static void init_check(void) {
/* Wait until we have our first readings from the ADC */
if (!adc_checked)
return;
/* Snapshot the current battery voltage */
v_bl_adc = v_bn_adc;
/* Exit state */
#ifdef DEBUG
uart_tx_bool(STR_V_BN_GT_V_L, v_bn_adc > V_L_ADC);
#endif
if (v_bn_adc > V_L_ADC)
charger_state = STATE_DIS_WAIT;
if ((v_bat_adc < V_L_ADC) || (v_sol_adc < V_SOL_MIN_ADC))
/* Battery/solar is low, begin charging */
enter_mains_chg();
else
charger_state = STATE_CHG_CHECK;
/* Run from solar */
enter_solar();
}
static void discharge_wait() {
#ifdef DEBUG
uart_tx_str(STR_DIS); uart_tx_str(STR_WAIT);
uart_tx_bool(STR_V_BN_LE_V_CL, v_bn_adc <= V_CL_ADC);
#endif
if (v_bn_adc <= V_CL_ADC)
/* Expire timer */
t_charger = 0;
/* Exit state if timer is expired */
#ifdef DEBUG
uart_tx_bool(STR_T_CHARGER, !t_charger);
#endif
if (!t_charger)
charger_state = STATE_DIS_CHECK;
}
static void charge_check() {
#ifdef DEBUG
uart_tx_str(STR_CHG); uart_tx_str(STR_CHK);
uart_tx_bool(STR_V_BN_LE_V_CL, v_bn_adc <= V_CL_ADC);
#endif
/* Still need to charge, when should we next check? */
if (v_bn_adc <= V_CL_ADC)
t_charger = T_HF_S;
else
t_charger = T_LF_S;
/* Critically high voltage check */
if (v_bn_adc >= V_CH_ADC) {
/* We must stop now! */
select_src(SRC_NONE);
charger_state = STATE_DIS_CHECK;
charger_warning = 0;
t_cwarn = 0;
/*!
* Checks whilst running on solar
*/
static void solar_check(void) {
if (v_bat_adc < V_L_ADC) {
/* Move to mains power */
enter_mains_chg();
return;
}
if (charge_source == SRC_NONE) {
/* Not yet charging, switch to primary source */
select_src(SRC_SOLAR);
/* As we have just started charging, reset warning timer */
exit_warning();
} else if (v_bn_adc <= (v_bl_adc + V_DELTA_ADC)) {
/* Check for high voltage threshold, are we there yet? */
#ifdef DEBUG
uart_tx_bool(STR_V_BN_GE_V_H, v_bn_adc >= V_H_ADC);
#endif
if (v_bn_adc >= V_H_ADC) {
/* We are done now */
select_src(SRC_NONE);
exit_warning();
return;
} else if (!t_cwarn) {
if (charger_warning) {
/*
* Situation still not improving,
* switch sources.
*/
select_src(SRC_ALT);
}
/* Reset our warning timer */
enter_warning();
}
} else if (!t_cwarn) {
/* Things are improving, reset warning if set. */
exit_warning();
}
v_bl_adc = v_bn_adc;
charger_state = STATE_CHG_WAIT;
}
static void charge_wait() {
#ifdef DEBUG
uart_tx_str(STR_CHG); uart_tx_str(STR_WAIT);
uart_tx_bool(STR_V_BN_GE_V_CH, v_bn_adc >= V_CH_ADC);
#endif
if (v_bn_adc >= V_CH_ADC)
/* Expire timer */
t_charger = 0;
/*!
* Checks whilst charging from mains
*/
static void mains_chg_check(void) {
if (v_bat_adc >= V_H_ADC) {
/* We've reached the float voltage */
enter_mains_float();
return;
}
}
#ifdef DEBUG
uart_tx_bool(STR_T_CHARGER, !t_charger);
#endif
if (!t_charger)
charger_state = STATE_CHG_CHECK;
/*!
* Checks whilst floating on mains
*/
static void mains_float_check(void) {
if (v_bat_adc < V_H_ADC) {
/* We've regressed, go back to charging state! */
enter_mains_chg();
return;
} else if ((!t_float) && (v_sol_adc >= V_SOL_MIN_ADC)) {
/* Solar can take it from here */
enter_solar();
}
}
/*!
@ -358,7 +242,7 @@ int main(void) {
/* Configure MOSFETs */
FET_PORT_DDR_REG = FET_PORT_DDR_VAL;
FET_PORT = 0;
FET_PORT = FET_MAINS | FET_SOLAR;
/* Turn on ADC and timers */
PRR &= ~((1 << PRTIM0) | (1 << PRTIM1) | (1 << PRADC));
@ -400,49 +284,8 @@ int main(void) {
/* One second passed, tick down the 1-second timers. */
if (!t_second) {
t_second = TIMER_FREQ;
if (t_charger)
t_charger--;
if (t_cwarn)
t_cwarn--;
}
if (t_adc)
t_adc--;
if (!t_led) {
if (v_bn_adc <= V_CL_ADC) {
/* Battery is critically low */
LED_PORT &= ~LED_BATT_HIGH;
LED_PORT ^= LED_BATT_GOOD;
} else if (v_bn_adc <= V_L_ADC) {
/* Battery is low */
LED_PORT &= ~(LED_BATT_HIGH|LED_BATT_GOOD);
} else if (v_bn_adc <= V_H_ADC) {
/* Battery is in "good" range */
LED_PORT &= ~LED_BATT_HIGH;
LED_PORT |= LED_BATT_GOOD;
} else if (v_bn_adc <= V_CH_ADC) {
/* Battery is above "high" threshold */
LED_PORT |= LED_BATT_HIGH;
LED_PORT &= ~LED_BATT_GOOD;
} else {
/* Battery is critically high */
LED_PORT ^= LED_BATT_HIGH;
LED_PORT &= ~LED_BATT_GOOD;
}
if (temp_adc < TEMP_MIN) {
LED_PORT |= LED_TEMP_LOW;
LED_PORT &= ~LED_TEMP_HIGH;
} else if (temp_adc < TEMP_MAX) {
LED_PORT ^= LED_TEMP_LOW;
LED_PORT &= ~LED_TEMP_HIGH;
} else {
LED_PORT &= ~LED_TEMP_LOW;
LED_PORT ^= LED_TEMP_HIGH;
}
t_led = T_LED_TICKS;
if (t_float)
t_float--;
}
if (!t_adc) {
@ -451,6 +294,28 @@ int main(void) {
while(ADCSRA & (1 << ADEN));
/* Temperature LED control */
if (temp_adc < TEMP_MIN) {
LED_PORT |= LED_TEMP_LOW;
LED_PORT &= ~LED_TEMP_HIGH;
} else if (temp_adc < TEMP_MAX) {
LED_PORT |= (LED_TEMP_LOW | LED_TEMP_HIGH);
} else {
LED_PORT &= ~LED_TEMP_LOW;
LED_PORT |= LED_TEMP_HIGH;
}
/*
* The "SOLAR" FET is no longer fitted, so this is more
* an indication of whether we consider solar to be
* "good enough". In short, it's just controlling the
* LED where the MOSFET was now.
*/
if (v_sol_adc < V_SOL_MIN_ADC)
FET_PORT |= FET_SOLAR;
else
FET_PORT &= ~FET_SOLAR;
/* Fan control */
if (t_fan) {
/* Kick-start mode */
@ -475,22 +340,29 @@ int main(void) {
OCR0A = 0;
}
/* Battery state LED control */
if (v_bat_adc <= V_L_ADC) {
LED_PORT &= ~LED_BATT_GOOD;
} else {
LED_PORT |= LED_BATT_GOOD;
}
/* Charger control */
switch (charger_state) {
case STATE_DIS_CHECK:
discharge_check();
case STATE_INIT:
init_check();
break;
case STATE_DIS_WAIT:
discharge_wait();
case STATE_SOLAR:
solar_check();
break;
case STATE_CHG_CHECK:
charge_check();
case STATE_MAINS_CHG:
mains_chg_check();
break;
case STATE_CHG_WAIT:
charge_wait();
case STATE_MAINS_FLT:
mains_float_check();
break;
default:
charger_state = STATE_DIS_CHECK;
charger_state = STATE_INIT;
}
}
}
@ -505,8 +377,8 @@ ISR(TIM1_COMPA_vect) {
if (t_second)
t_second--;
if (t_led)
t_led--;
if (t_adc)
t_adc--;
}
ISR(ADC_vect) {
@ -519,20 +391,14 @@ ISR(ADC_vect) {
ADCSRA |= (1 << ADSC);
break;
case ADC_MUX_BATT:
v_bn_adc = adc;
#if 0
/* Not being used for now */
v_bat_adc = adc;
ADMUX = ADC_MUX_SOLAR;
ADCSRA |= (1 << ADSC);
break;
case ADC_MUX_SOLAR:
adc_solar = adc;
ADMUX = ADC_MUX_MAINS;
ADCSRA |= (1 << ADSC);
break;
case ADC_MUX_MAINS:
adc_mains = adc;
#endif
v_sol_adc = adc;
/* Once we get here, we've done a full cycle */
adc_checked = 1;
default:
ADMUX = ADC_MUX_TEMP;
ADCSRA &= ~(1 << ADEN);

View File

@ -28,11 +28,6 @@
*/
#define T_ADC_MS (250)
/*!
* How long before we change LED states? Milliseconds.
*/
#define T_LED_MS (150)
/*
* Temperature ranges and fan PWM settings
*/
@ -57,53 +52,29 @@
/* --- Thresholds --- */
/*!
* Critically high battery voltage. Exceeding this voltage could damage
* the battery or the equipment downstream of it.
*/
#define V_CH_MV (15500)
/*!
* High battery voltage. If we reach this voltage and the charger
* stops, just switch to discharge mode, consider the job done.
*/
#define V_H_MV (14000)
#define V_H_MV (14400)
/*!
* Low battery voltage. If the voltage dips to or below this level, we
* should turn the charger on.
*/
#define V_L_MV (13800)
#define V_L_MV (12400)
/*!
* Critically low battery voltage. If we reach this level, we need to
* urgently turn the charger on and need to be ready to switch sources
* in a hurry if the chosen source isn't charging.
* Solar minimum voltage. If the solar is below this threshold, we
* consider it too low to reliably charge the system.
*/
#define V_CL_MV (11800)
/*!
* Battery minimum charge step. This is the amount of charge we expect
* the battery to have increased by before we consider flagging a
* warning.
*/
#define V_DELTA_MV (50)
#define V_SOL_MIN_MV (18000)
/* --- Timeouts --- */
/*!
* High frequency polling period in seconds.
* How long do we remain on the mains charger after reaching V_H_MV?
*/
#define T_HF_S (15)
/*!
* Low frequency polling period in seconds.
*/
#define T_LF_S (60)
/*!
* How long before we consider a lack of voltage increase a warning?
*/
#define T_CWARN_S (10)
#define T_FLOAT_S (3600)
#endif