
BMU v2
KomplexKapharnaum/KXKM_Batterie_Parallelator
Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app

Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app
BMU v1 is an open-source printed circuit board by KomplexKapharnaum, from the KomplexKapharnaum/KXKM_Batterie_Parallelator repository on GitHub, published under the GPL-3.0 licence. It is a 4-layer board of 193.5 × 105 mm with 143 parts on 105 nets, routed with 1,042 tracks and 474 vias.
It is built around the VPS8505, ISO1540DR-HXY and TCA9517ADGK, with the TCA9535PWR(UMW), 1N4148W and HOVB4026-5W-2MR-1% among its other parts. Its parts include 43 resistors, 24 diodes, 20 capacitors, 9 ICs and 5 connectors.
Battery Management Unit for parallel 24-30V packs
Up to 32 batteries · ESP32-S3 · BLE + MQTT + InfluxDB · iOS companion app
The KXKM Batterie Parallelator is an embedded Battery Management Unit (BMU) that safely parallels multiple battery packs (24-30V) for off-grid stage installations. Each battery is individually monitored (voltage, current, temperature) and can be disconnected in microseconds via MOSFET switches.
Built by L'Electron Rare for KompleX KapharnaüM (Villeurbanne, France) — a live arts company deploying digital scenography in public spaces without grid power.
| Feature | Description |
|---|---|
| Per-battery protection | Under/over-voltage, over-current, imbalance detection |
| Auto-reconnection | Exponential backoff with permanent lock after 5 faults |
| Soft-balancing | Duty-cycling with opportunistic R_int measurement |
| Victron integration | BLE Instant Readout + GATT SmartShunt emulation + device scanner |
| Touchscreen UI | 320x240 LVGL display with battery grid, SOH, charts |
| iOS companion app | BLE real-time monitoring, offline cache, role-based access |
| Cloud telemetry | MQTT + InfluxDB + Grafana with offline persistence |
| OTA updates | Dual-partition with automatic rollback |
# ESP-IDF firmware (production)
cd firmware-idf
source ~/esp/esp-idf/export.sh
idf.py build
idf.py -p /dev/cu.usbmodem* flash monitor
# PlatformIO (legacy Arduino)
pio run -e kxkm-s3-16MB --target upload
# Run all tests (no hardware needed)
pio test -e sim-host # 13 PlatformIO tests
cd firmware-idf && idf.py build # 13 ESP-IDF host tests
# Cloud stack (kxkm-ai server)
cd kxkm-api && cp .env.example .env && docker compose up -d ┌──────────────────────────┐
│ ESP32-S3-BOX-3 │
│ 320x240 LVGL display │
│ NimBLE + WiFi │
└─────────┬────────────────┘
│ I2C 50kHz
┌───────────────┼───────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ TCA9535 │ │ TCA9535 │ │ ...×8 │
│ 4× MOSFET │ │ 4× LED │ │ │
└─────┬─────┘ └───────────┘ └───────────┘
│
┌─────▼─────┐
│ INA237 ×4 │ ← per TCA (voltage + current)
└───────────┘
┌──────────────────────────────────────────────────────┐
│ Cloud Stack │
│ BMU ──MQTT──► Mosquitto ──► Telegraf ──► InfluxDB │
│ │ │
│ iPhone ◄──BLE──► BMU Grafana ◄───────────┘ │
│ iPhone ◄──REST──► FastAPI ◄─────────────────┘ │
└──────────────────────────────────────────────────────┘| Component | Role |
|---|---|
bmu_protection | State machine: 5 states, 5 criteria, permanent lock |
bmu_balancer | Soft-balancing duty-cycling + R_int measurement |
bmu_ina237 | 16× INA237 power monitors (2mΩ shunt) |
bmu_tca9535 | 8× GPIO expanders (switches + LEDs) |
bmu_ble | NimBLE 4 GATT services + Victron SmartShunt |
bmu_ble_victron | Victron Instant Readout (AES-CTR encrypted ads) |
bmu_ble_victron_scan | BLE central scanner for Victron devices |
bmu_display | LVGL: battery grid, pack info, SOH, charts, swipe |
bmu_influx + _store | InfluxDB client + offline FAT/SD persistence |
bmu_mqtt | ESP-MQTT with auth credentials |
bmu_vedirect | Victron VE.Direct UART parser (solar charger) |
bmu_rint | Internal resistance pulse measurement |
bmu_web | HTTP + WebSocket with token auth + rate limiting |
bmu_config | NVS runtime config + Victron device keys |
bmu_storage | NVS, FAT, SPIFFS, SD card, USB MSC |
bmu_ota | OTA firmware update with rollback |
Nb_switch < 5 → Reconnect immediately when condition clears
Nb_switch == 5 → 10-second delay before reconnection
Nb_switch > 5 → Permanent lock (blinking red LED) until reboot| Threshold | Default | Configurable |
|---|---|---|
| Min voltage | 24,000 mV | via Kconfig + NVS |
| Max voltage | 30,000 mV | via Kconfig + NVS |
| Max current | 10,000 mA | via Kconfig + NVS |
| Imbalance | 1,000 mV | via Kconfig + NVS |
| Reconnect delay | 10,000 ms | via Kconfig |
| Switch limit | 5 | via Kconfig |
| State | LED |
|---|---|
| Connected | Solid green |
| Disconnected | Solid red |
| Error | Blinking red (~1 Hz) |
| Locked | Solid red |
KXKM BMU — SwiftUI + CoreBluetooth
| Feature | Transport |
|---|---|
| Real-time battery dashboard | BLE (2s poll) |
| Battery detail + voltage chart | BLE |
| Switch ON/OFF + reset | BLE (role-gated) |
| Protection config (0.025V/A step) | BLE |
| WiFi config for BMU | BLE |
| Audit trail + filtering | Local SQLDelight |
| Offline mode with cache indicator | Auto-fallback |
| SOH dashboard + R_int | BLE + REST |
| Victron device scanning | BLE passive |
3 roles: Admin (full), Technician (control), Viewer (read-only) — PIN + Face ID.
Code: iosApp/ (Xcode, CoreBluetooth) + kxkm-bmu-app/ (KMP Shared Kotlin)
The BMU interacts with the Victron ecosystem via three mechanisms:
| Mechanism | Direction | Protocol |
|---|---|---|
| Instant Readout | BMU → VictronConnect | AES-CTR encrypted advertising (PID 0xA389) |
| GATT SmartShunt | BMU → any BLE client | 9 read-only characteristics (V, I, SOC, Ah, TTG, T, alarm) |
| Device Scanner | Victron devices → BMU | Passive BLE scan, AES decrypt, 8 device cache |
Supported Victron record types: Solar (0x01), Battery (0x02), Inverter (0x03), DC-DC (0x04).
Docker stack on kxkm-ai server:
| Service | Port | Role |
|---|---|---|
| Mosquitto | 1883, 9001 | MQTT broker (authenticated) |
| InfluxDB 2.7 | 8086 | Time-series storage (org=kxkm, bucket=bmu) |
| Telegraf | - | MQTT → InfluxDB bridge |
| FastAPI | 8400 | REST API (sync, history, audit) |
| Grafana 11 | 3001 | 3 dashboards (live, fleet, solar) |
Setup: cp .env.example .env — fill all secrets (API key, InfluxDB token, MQTT credentials, Grafana password, CORS origins). All secrets via ${...} env vars, chmod 600 .env.
Offline resilience: when WiFi/InfluxDB is unreachable, telemetry is persisted to internal FAT flash (/fatfs/influx/) with 2-file rotation (512KB each). Automatic replay on reconnection.
26 tests total — no hardware required.
pio test -e sim-host| Suite | Tests | Covers |
|---|---|---|
test_protection | 10 | V/I/imbalance/lock state machine |
test_battery_route_validation | - | Index bounds + state checks |
test_influx_buffer_codec | - | InfluxDB line-protocol encoding |
test_web_mutation_rate_limit | 3 | Sliding window + multi-IP |
test_web_route_security | 9 | Constant-time token + Bearer |
test_ws_auth_flow | 7 | Combined auth + rate limit |
test_mqtt_influx_codec | 3 | JSON MQTT + topic parsing |
test_emulation_bench | - | Emulation benchmark |
cd firmware-idf && idf.py build| Suite | Tests | Covers |
|---|---|---|
test_protection | 13 | Full state machine (ESP-IDF) |
test_victron_gatt | 8 | GATT encoding (V, SOC, alarm, TTG, T) |
test_victron_scan | 5 | Payload parsing, expiry, MAC, CID |
test_ble_victron | - | Battery/solar payload encoding |
test_vedirect_parser | - | VE.Direct frame parsing |
test_rint | - | R_int calculation |
test_config_labels | - | Battery label management |
test_vrm_topics | - | VRM topic generation |
| Pipeline | Trigger | Gate |
|---|---|---|
ci.yml | push/PR | sim-host 13 tests pass |
sim-host-tests.yml | push/PR | + S3 build + RAM ≤75% + Flash ≤85% |
esp-idf-ci.yml | push/PR | + ESP-IDF host tests + flash ≤85% of 2MB OTA |
Current flash usage: 81% (1.69 MB / 2 MB OTA partition) — 19% headroom.
All findings resolved as of 2026-04-07.
| ID | Finding | Status |
|---|---|---|
| CRIT-001 | Obsolete function calls | ✅ |
| CRIT-002 | Unreliable global Nb_INA | ✅ |
| CRIT-003 | I2C access without mutex | ✅ |
| CRIT-004 | Missing mutex init | ✅ |
| HIGH-005 | WebSocket V/I race | ✅ |
| HIGH-006 | reconnect_time race | ✅ |
| HIGH-007 | Uninitialized globals | ✅ |
| ID | Severity | Finding | Fix |
|---|---|---|---|
| CRIT-A | Critical | mV/V unit mismatch | ESP-IDF: mV everywhere |
| CRIT-B | Critical | Imbalance vs config instead of fleet max | bmu_protection |
| CRIT-C | Critical | Deadlock in web switch | bmu_web |
| CRIT-D | Critical | Unauthenticated routes | bmu_web_security |
| HIGH-1 | High | Negative overcurrent ignored | Bidirectional factor |
| HIGH-2 | High | Unauthenticated WebSocket | Token on first frame |
| HIGH-3 | High | XSS in /log | cJSON null check |
| HIGH-4 | High | MQTT plaintext | Mosquitto auth + .env |
| HIGH-5 | High | I2C speed change unguarded | I2CLockGuard |
| HIGH-7 | High | Public battery_voltages[] | state_mutex |
| HIGH-8 | High | Influx task crash on SD fail | influx_store fallback |
| MED-1 | Medium | No permanent lock (F08) | BMU_STATE_LOCKED |
| MED-010 | Medium | mV/V harmonization | mV throughout |
| Finding | Fix |
|---|---|
| Mosquitto anonymous access | Auth + password file |
| Hardcoded secrets in docker-compose | All via .env (chmod 600) |
| Default InfluxDB token | Rotated (44-char random) |
| CORS allow_origins=["*"] | Restricted via env var |
| Offline data loss | FAT/SD persistence + replay |
| Version | Status | Files |
|---|---|---|
| BMU v1 | Manufactured | hardware/PCB/ |
| BMU v2 | Production-ready (ERC 0, BOM 107) | hardware/pcb-bmu-v2/ |
| IC | Package | Role | I2C Address |
|---|---|---|---|
| INA237 | VSSOP-10 | Power monitor (V/I/P) | 0x40-0x4F (16 max) |
| TCA9535 | TSSOP-24 | GPIO expander (switches + LEDs) | 0x20-0x27 (8 max) |
| ISO1540 | SOIC-8 | Galvanic I2C isolator | - |
| IRF4905 | D2PAK | P-channel MOSFET (55V, 74A) | - |
TCA_0 (0x20) → INA 0x40-0x43 → Batteries 1-4
TCA_1 (0x21) → INA 0x44-0x47 → Batteries 5-8
TCA_2 (0x22) → INA 0x48-0x4B → Batteries 9-12
TCA_3 (0x23) → INA 0x4C-0x4F → Batteries 13-16Topology constraint: Nb_TCA × 4 == Nb_INA — mismatch triggers fail-safe (all OFF).
KXKM_Batterie_Parallelator/
├── firmware-idf/ # ESP-IDF 5.4 firmware (25 components)
│ ├── components/ # Modular ESP-IDF components
│ ├── main/ # app_main entry point
│ └── test/ # Unity host tests (13 suites)
├── firmware/ # Legacy PlatformIO/Arduino firmware
│ ├── src/ # Source modules
│ ├── test/ # sim-host tests (8 suites, 13 tests)
│ └── lib/INA237/ # Local INA237 driver fork
├── iosApp/ # iOS SwiftUI app (CoreBluetooth)
├── kxkm-bmu-app/ # KMP shared Kotlin module
│ └── shared/ # Cross-platform domain logic
├── kxkm-api/ # Docker stack (Mosquitto, InfluxDB, FastAPI, Grafana)
├── hardware/ # KiCad schematics + Gerbers (v1 + v2)
├── specs/ # Kill_LIFE gates (S0-S3)
├── docs/ # Specs, plans, governance, research
├── grafana/dashboards/ # 3 Grafana dashboard JSONs
└── scripts/ # CI, QA, ML pipelineKompleX KapharnaüM is a live arts company based in Villeurbanne (Lyon, France), active for 20+ years in street arts, live performance, and digital creation. The company develops its own embedded hardware platform (K32, ESP32-based) and maintains open-source tools on GitHub.
GNU General Public License v3.0
Built with ESP-IDF, LVGL, NimBLE, SwiftUI, FastAPI, InfluxDB, and Grafana.
Developed by L'Electron Rare for KompleX KapharnaüM
| Qty | Value | Footprint | References |
|---|---|---|---|
| 19 | SolderJumper_3_Open | Jumper:SolderJumper-3_P1.3mm_Open_RoundedPad1.0x1.5mm_NumberLabels | JP2, JP3, JP4, JP5, JP6, JP10, JP11, JP12, JP13, JP14, JP15, JP16, JP17, JP18, JP19, JP20 +3 |
| 10 | 0.1uF | Capacitor_SMD:C_0805_2012Metric | C2, C3, C4, C5, C6, C7, C8, C9, C10, C11 |
| 8 | 10R | Resistor_SMD:R_0603_1608Metric | R3, R4, R10, R11, R17, R18, R37, R38 |
| 8 | 1N4148W | Diode_SMD:D_SOD-523 | D2, D3, D6, D7, D10, D11, D14, D15 |
| 8 | 330R | Resistor_SMD:R_0603_1608Metric | R29, R30, R31, R32, R33, R34, R35, R36 |
| 8 | 470R | Resistor_SMD:R_0603_1608Metric | R2, R5, R9, R12, R16, R19, R23, R39 |
| 8 | MountingHole_Pad | MountingHole:MountingHole_5mm_Pad | H1, H2, H3, H4, H5, H6, H7, H8 |
| 5 | MountingHole | MountingHole:MountingHole_3.2mm_M3_DIN965 | H9, H10, H13, H13, H14 |
| 4 | 10k | Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P5.08mm_Horizontal | R7, R14, R21, R41 |
| 4 | 470R | Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P5.08mm_Horizontal | R6, R13, R20, R40 |
| 4 | BLUE | LED_SMD:LED_0603_1608Metric | D4, D8, D12, D16 |
| 4 | GREEN | LED_SMD:LED_0603_1608Metric | D21, D23, D25, D27 |
| 4 | HOVB4026-5W-2MR-1% | easyeda2kicad:RES-SMD_4P-L10.8-W6.7 | R1, R8, R46, R47 |
| 4 | INA237 | Package_SO:VSSOP-10_3x3mm_P0.5mm | U1, U2, U3, U7 |
| 4 | IRFZ44NPBF | Package_TO_SOT_THT:TO-220-3_Horizontal_TabUp | Q5, Q6, Q7, Q8 |
| 4 | RED | LED_SMD:LED_0603_1608Metric | D20, D22, D24, D26 |
| 3 | 10uF | Capacitor_SMD:C_0805_2012Metric | C14, C20, C21 |
| 3 | 1A/100V | Diode_SMD:D_SMA | D5, D18, D19 |
| 2 | 10uF/50V | Capacitor_SMD:C_1206_3216Metric | C15, C16 |
| 2 | 1K | Resistor_SMD:R_0603_1608Metric | R26, R27 |
| 2 | 50R | Resistor_SMD:R_0603_1608Metric | R24, R25 |
| 2 | MountingHole_Pad | MountingHole:MountingHole_3.2mm_M3_Pad_Via | H11, H12 |
| 2 | RJ45_Shielded | easyeda2kicad:RJ45-TH_RC01061 | J1, J2 |
| 2 | Screw_Terminal_01x01 | easyeda2kicad:TH-8_L20.0-W16.0-P5.60_AMT0860001TA0000G | J11, J12 |
| 1 | 0,1uF | Capacitor_SMD:C_0805_2012Metric | C13 |
| 1 | 0.1uF | Capacitor_SMD:C_0603_1608Metric | C18 |
| 1 | 10K | Resistor_SMD:R_0603_1608Metric | R28 |
| 1 | 10k/1% | Resistor_SMD:R_0603_1608Metric | R22 |
| 1 | 10uF,16V | Capacitor_SMD:CP_Elec_8x10 | C1 |
| 1 | 1N4148WT | Diode_SMD:D_SOD-523 | D9 |
| 1 | 1uF | Capacitor_SMD:C_0805_2012Metric | C12 |
| 1 | 20pF | Capacitor_SMD:C_0603_1608Metric | C19 |
| 1 | 22uH,1.5A | Inductor_SMD:L_Taiyo-Yuden_NR-50xx | L1 |
| 1 | 52.3k/1% | Resistor_SMD:R_0603_1608Metric | R15 |
| 1 | Conn_01x02_Pin | Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical | J3 |
| 1 | ISO1540DR | Package_SO:SOIC-8_3.9x4.9mm_P1.27mm | U5 |
| 1 | LOGO | LOGO | G*** |
| 1 | MCP16331T-E/CH | Package_TO_SOT_SMD:SOT-23-6 | U10 |
| 1 | SolderJumper_2_Open | Jumper:SolderJumper-2_P1.3mm_Open_RoundedPad1.0x1.5mm | JP1 |
| 1 | TCA9517ADGK | easyeda2kicad:VSSOP-8_L3.0-W3.0-P0.65-LS5.0-BL | U4 |
| 1 | TCA9535PWR | Package_SO:TSSOP-24_4.4x7.8mm_P0.65mm | U6 |
| 1 | VPS8505 | easyeda2kicad:SOT-23-6_L2.9-W1.6-P0.95-LS2.8-BR | U8 |
| 1 | VPT85BB-01A | Transformer_SMD:VP85BB-01A | TR1 |

KomplexKapharnaum/KXKM_Batterie_Parallelator
Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app

KomplexKapharnaum/KXKM_Batterie_Parallelator
Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app

KomplexKapharnaum/KXKM_Batterie_Parallelator
Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app

KomplexKapharnaum/KXKM_Batterie_Parallelator
Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app

KomplexKapharnaum/KXKM_Batterie_Parallelator
Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app

KomplexKapharnaum/KXKM_Batterie_Parallelator
Battery Management Unit for parallel 24-30V packs Up to 32 batteries ESP32-S3 BLE + MQTT + InfluxDB iOS companion app


About-Time-Technologies/MHCI
Modular Hardware Control Interface - Components for the Events Industry

mauro-starspec/Elevation_Pivot_Daughter-Board
KiCad 9 daughter board for the Starspec elevation/pivot controller.


bot-hoven/bot-hoven-electrical
Electrical substem for Bot-Hoven. Includes KiCad files containing schematics and PCB designs for components in Bot-Hoven.

Dhruv2212-star/9-axis-IMU-barometer-Data-logger-
This project is a compact, battery-powered data logger designed to record motion and environmental data in a single device.

Electro707/p1200_lcd_backpack
My version of an LCD Backpack with some unique features

It measures 193.5 × 105 mm and has 4 copper layers, 1.6 mm thick.
143 parts, built around the VPS8505, ISO1540DR-HXY, TCA9517ADGK and TCA9535PWR(UMW). The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
From the KomplexKapharnaum/KXKM_Batterie_Parallelator repository on GitHub, at the commit this page was made from: the board is hardware/PCB/BMU v1/BMU v1.kicad_pcb and its schematic hardware/PCB/BMU v1/BMU v1.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Its repository publishes it under the GPL-3.0 licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).