Charger v1
An experimental four-channel smart charger based on Arduino Mega 2560. Full firmware description: pinout, control loop, charge profiles, protections.
Overview
Charger v1 was created as an engineering test platform for developing charging algorithms, measuring battery parameters and testing different battery chemistries.
Each channel has separate battery voltage, current and temperature measurement. Control is based on PWM regulation of a buck module through its feedback path, with pre-charge, constant-current and constant-voltage stages. It includes profiles for Li-Ion, LiFePO4 and NiMH/NiCd cells.
The device uses two ST7920 displays, four INA219 measurement modules, NTC temperature sensors, LED indication, a buzzer and automatic brightness control.
4 channels Li-Ion LiFePO4 NiMH CC/CV Arduino Mega 2560The project archive includes source code, a wiring diagram, a block diagram and a buck-module feedback injection diagram.
Check the wiring, calibrate measurements and test every channel before connecting batteries.
1Single-channel hardware
The same set is repeated for each of the 4 channels.
| Block | Component | Purpose |
|---|---|---|
| Step-down converter | buck LM2596 / XL4015 (with FB exposed) | adjustable voltage source |
| Switch | MOSFET IRLZ44N / AO3400 (logic-level) | breaks battery “+” — for OCV/-dV sensing and safety |
| V/I measurement | INA219 (0.1 Ω shunt) | battery voltage and charge current |
| Temperature | NTC 10k β=3950 + 10k divider to +5V | cell over-temperature protection |
| Holder | 18650 cradle / terminals | the battery itself |
2Control principle — FB injection
Current and voltage are regulated not by a “smart” buck controller, but by injecting a potential into the feedback (FB) input of an ordinary buck module:
Raising the FB voltage makes the buck lower its output. The relationship is inverted: ↑PWM → ↓Vout. The inversion sign is handled in the PI controller (subtraction). PWM_MAX = 255 corresponds to minimum output voltage — the default safe state.
3Arduino Mega 2560 pinout
Charge channels — 4 lines per channel
| Channel | PWM→FB | MOSFET EN | NTC (ADC) | INA219 addr |
|---|---|---|---|---|
| Ch1 | D5 OC3A | D32 | A0 | 0x40 |
| Ch2 | D2 OC3B | D33 | A1 | 0x41 |
| Ch3 | D3 OC3C | D34 | A2 | 0x44 |
| Ch4 | D6 OC4A | D35 | A3 | 0x45 |
INA219 addresses are set by the A0/A1 jumpers on the modules. MOSFET EN = HIGH → current flows into the battery.
ST7920 128×64 displays (software SPI)
(clock, data, cs, reset): E=SCLK, RW=MOSI, RS=CS.| Display | SCK (E) | MOSI (RW) | CS (RS) | RESET | Channels |
|---|---|---|---|---|---|
| Display 0 | D48 | D47 | D46 | D8 | Ch1, Ch2 |
| Display 1 | D52 | D51 | D53 | D7 | Ch3, Ch4 |
PSB = GND (SPI mode). Backlight BLA of both displays → D9 (PWM, through a transistor, not directly).
I²C bus · 100 kHz
SDA = D20 SCL = D21
Shared by 4× INA219 + VEML7700 (0x10).
Power
- +5V rail ≥ 1 A — Mega + all modules
- +6…12V — buck VIN input (≈1.5 A/channel)
- Common ground for Mega / buck / batteries — required
UI and indication
| Function | Pin | Note |
|---|---|---|
| MODE button | D30 | INPUT_PULLUP, button to GND |
| START button | D31 | INPUT_PULLUP, button to GND |
| Buzzer | D12 | moved from D9 |
| ST7920 backlight | D9 | analogWrite (PWM) |
| LED READY (green) | D10 | → 330Ω → LED → GND |
| LED FAULT (red) | D11 | → 330Ω → LED → GND |
LED panel — 8 LEDs, port PA
D22–D29 = G G B B Y Y R R, each through 330Ω to GND. The pairs R2 R1 Y2 Y1 B2 B1 G2 G1 form the “SoC bar” of the selected channel, filled right-to-left (red → green).
4PWM setup (fast PWM)
- Timer3 (D2/D3/D5) and Timer4 (D6): Fast PWM 8-bit, non-inverting, prescaler /1 → ≈62.5 kHz.
- Timer0 is left untouched —
millis()/delay()keep working. - Values are written directly to the
OCR3AL/BL/CL,OCR4ALregisters viasetPwmFast(). - The high frequency gives ≈1 mV ripple after the RC filter.
5Charge state machine (per channel)
Lithium:
NiMH:
| State | What it does |
|---|---|
IDLE | waits for START, MOSFET open |
CHECK | opens the switch, reads OCV, checks presence/reverse polarity, estimates starting SoC |
PRECHARGE | trickle 10% current while V < v_precharge (deep-discharge recovery) |
CC | constant current = setpoint (PI on current) up to V_max |
CV | constant voltage V_max until current drops below i_term_pct |
NIMH_CC | CC only + -dV / dT/dt monitoring |
TERM_CHECK | once a minute opens the switch, reads OCV for -dV detection |
DONE | charge complete, capacity is computed |
FAULT | fault; no auto-reset — the operator presses START |
PI controller
Shared for current and voltage. Integral anti-windup, ±1000 clamp, rate-limit of 5 PWM steps per iteration (protects against current spikes on entering CC).
Capacity estimate
From mAh and starting SoC (OCV table): Cap ≈ mAh·100/(100−SoC). If SoC>30% — rough (“~”), if ≥90% not computed.
6Chemistry profiles
Values per single cell (1S).
| Parameter | Li-Ion | LiFePO4 | NiMH |
|---|---|---|---|
| V_max (CV) | 4.20 V | 3.55 V | 1.55 V (limit) |
| V_precharge | 3.00 V | 2.50 V | 0.90 V |
| Precharge current | 10% | 10% | 20% |
| CV termination current | 5% | 7% | — (no CV) |
| -dV | — | — | 10 mV |
| dT/dt | — | — | 1 °C/min |
| Timeout | 240 min | 240 min | 300 min |
| Reverse | <100 mV | <100 mV | <100 mV |
The charge current is selected by button from a list: 500 / 1000 mA (I_TARGET_LIST_MA).
7Protections
Checked on every state-machine step.
- WDT 4 s —
wdt_enable(WDTO_4S) - Over-voltage: V > V_max + 100 mV →
F_OVERVOLT - Hard current cutoff: I > 1500 mA →
F_OVERCURRENT - Temperature: T > 45 °C →
F_TEMP_HIGH - Temperature: T < 0 °C →
F_TEMP_LOW(3° hysteresis) - NTC open (ADC <20 or >1000) →
F_NTC_OPEN
- Reverse polarity (OCV < v_reverse) →
F_REVERSE - Battery removed (V < 500 mV) →
F_NO_BATTERY - Profile timeout →
F_TIMEOUT - INA219 lost →
F_INA_LOST - PWM stuck at a limit >5 s with large error →
F_LOOP_STUCK - MOSFET open in all states except PRECHARGE/CC/CV/NIMH_CC
8Measurement
- INA219: 32V/2A calibration (100 µA/bit), IIR EMA filter
y += (x−y)/4for V and I. - NTC → °C: β-equation,
R_ntc = 10k·ADC/(1023−ADC), result in °C×10, extra smoothing(old·3+new)/4. - OCV: opens the MOSFET, 200 ms relaxation, reads VBUS. Does not re-close the switch — the state machine does that.
9Interface
Screens: Display 0 — channels 1/2 (top/bottom), Display 1 — channels 3/4. Per channel: name+chemistry+current, large V/A, state/T/accumulated mAh, progress bar.
MODE button (D30)
- short — switch channel 1→2→3→4
- long — enter/exit edit (in IDLE/DONE/FAULT)
- in edit, short — next field (chemistry → current → exit)
START button (D31)
- short — START/STOP the channel
- in edit — change the field value
Sound: short beep — switch; beep — START/STOP; long — DONE; short — FAULT.
Auto-brightness: the VEML7700 is read once per second; the backlight smoothly ramps to a target based on ambient light (in the dark — minimum 20/255, in room light — maximum).
10Assembly notes
- The MOSFET must be logic-level (Vgs(th) < 3 V), otherwise it won’t turn on from 5 V.
- Tune the injection resistor: PWM 30…220 → buck output 3.0…4.3 V (for an 18650) — regulation headroom.
- First-run check over Serial (115200): 4×
INA[x] OK+VEML OK. - At PWM=255 the buck output is ≤ 3 V, at PWM=0 — maximum (~4.3–4.5 V).
- Display PSB to GND, common ground is mandatory.