BattPulse

Securing the Energy Transformation

Leader Battery Management System
Developer API Reference

CAN Bus Protocol WiFi HTTP/JSON API 6S–126S

Complete protocol reference for the BattPulse Leader BMS.
CAN Bus telemetry, WiFi HTTP/JSON API, command protocol, and code examples.

Document: BP-BMS-001-API  |  Revision 1.1  |  March 2026
© 2026 BattPulse by SONAL  |  info@battpulse.com  |  battpulse.com

📄 Get the latest version of all documents
📚 BattPulse Leader BMS Documentation
Quick Start Guide  |  Developer API Reference  |  All Documents  |  🛒 Get the BMS

Contents

  1. Introduction
  2. CAN Bus Protocol — Overview
  3. CAN Bus Protocol — Frame Reference
  4. CAN Bus Protocol — Command Frame
  5. CAN Bus Protocol — Code Examples
  6. WiFi HTTP/JSON API — Overview
  7. WiFi HTTP/JSON API — Endpoints
  8. WiFi HTTP/JSON API — Code Examples
  9. Data Model Reference
  10. BattPulse Display Compatibility
  11. Troubleshooting

1. Introduction

The BattPulse Leader BMS is a professional-grade, automotive-rated battery management system. It features a high-accuracy 16-bit analogue front-end for cell monitoring, high-current shunt-based current measurement, and a powerful dual-core microcontroller. It supports up to 126S lithium battery configurations (see product notes) with real-time cell monitoring, balancing, overcurrent protection, and multiple temperature probes.

The BMS exposes its telemetry data and accepts commands through two interfaces:

Method Transport Speed Best For
CAN Bus CAN 2.0B (11-bit) 100 ms cycle (10 Hz) BattPulse Display 7″, LCD displays, data loggers, vehicle ECUs, industrial systems
WiFi HTTP POST / JSON ~250 ms cycle Web dashboards, mobile apps, BattPulse Display 7″, remote monitoring
🔌 Who Is This Document For?

This API reference is for system integrators, display developers, and embedded engineers who want to read BMS data or send commands to the BattPulse Leader BMS. Use these protocols to build custom dashboards, data loggers, or connect the BattPulse Display 7″.

🛒 Get the BattPulse Leader BMS

Ready to get started? Order the BattPulse Leader BMS directly from our store.

battpulse.com/product/battpulse-leader-battery-management-system →

2. CAN Bus Protocol — Overview

Physical Layer

ParameterValue
StandardCAN 2.0B (11-bit standard identifiers)
Baud Rate500 kbps
Byte OrderLittle-endian (LSB first)
TransceiverAutomotive-grade CAN transceiver (3.3 V logic)
Termination120 Ω required at each end of the bus
Cycle Time100 ms (all frames transmitted every cycle)
Frame FormatStandard (11-bit), no extended, no RTR

CAN Bus Connector

The CAN bus shares a connector with the shunt interface. See the Shunt and CAN Communication connector (Micro-Fit 3.0 — 43025-0600):

Shunt and CAN Communication Connector
PinSignalDescription
13V33.3 V reference (shunt power)
2SCLI2C clock (shunt)
3SDAI2C data (shunt)
4CAN-HCAN High signal
5CAN-LCAN Low signal
6GNDGround (shunt reference)
⚠️ CAN Bus Wiring
The CAN connector does not carry a dedicated CAN GND pin. The CAN-H and CAN-L differential pair is sufficient for communication. If your external device requires a ground reference, use a separate ground connection to the system chassis. Or you can use the GND pin next to it (not preffered for preventing ground loop in the system)

CAN ID Map

CAN ID(s) Direction Frame Name DLC Cycle
0x300 BMS → External Pack Status (V, I, SOC, Status) 7 100 ms
0x301 BMS → External Status Extended (MaxC, MinC, Temp extremes) 8 100 ms
0x302 BMS → External Energy (capacity, SOH) — Reserved, not yet sent
0x3300x337 BMS → External Cell Voltages (2 cells per frame) 4 100 ms
0x3500x351 BMS → External Temperature Probes (4 per frame) 8 100 ms
0x360 BMS → External I/O State (FETs, Balancing, Digital Inputs) 2 100 ms
0x370 BMS → External Fault / Warning Bitmasks 4 100 ms
0x3A0 External → BMS Command Frame (Restart) 5 On demand
ℹ️ Compatible with BattPulse Display 7″
These CAN IDs match the BattPulse Display 7″ protocol exactly. Connect the display to the BMS CAN bus, set baud rate to 500 kbps, and data appears automatically. Get the BattPulse Display 7″

3. CAN Bus Protocol — Frame Reference

Frame 0x300 — Pack Status

BMS → External  |  DLC: 7  |  100 ms cycle
ByteTypeScaleUnitDescription
0–1uint16÷ 100VPack Voltage — sum of all cell voltages (e.g. 5120 = 51.20 V)
2–3int16÷ 10APack Current (+ = charging, − = discharging)
4–5uint16÷ 10%SOC (e.g. 850 = 85.0%)
6uint8Status: 0=Idle, 1=Charging, 2=Discharging, 3=Fault

Status Byte Values

ValueMeaningCondition
0IdleCurrent within ±0.5 A dead-band
1ChargingCurrent > +0.5 A
2DischargingCurrent < −0.5 A
3FaultAny fault flag active (overvoltage, undervoltage, overtemp, powerdown)
⚠️ Current Sign Convention
On the CAN bus, positive = charging, negative = discharging. This matches the BattPulse Display convention.

Raw byte example — Pack at 51.20 V, charging at 15.0 A, SOC 85.0%:

Byte:  [0]   [1]   [2]   [3]   [4]   [5]   [6]
Hex:   0x00  0x14  0x96  0x00  0x52  0x03  0x01
       <--5120-->  <--150--->  <--850--->  Status=Charging

Frame 0x301 — Pack Status Extended

BMS → External  |  DLC: 8  |  100 ms cycle
ByteTypeScaleUnitDescription
0–1uint16÷ 1000VHighest cell voltage (e.g. 3650 = 3.650 V)
2–3uint16÷ 1000VLowest cell voltage (e.g. 3580 = 3.580 V)
4–5int16÷ 10°CHighest temperature across all probes
6–7int16÷ 10°CLowest temperature across all probes
ℹ️ Temperature Aggregation
Temperature extremes consider all active sources: internal die temperature, board temperature sensor (if present), shunt temperature (if shunt mode), and all external NTC probes.

Frame 0x302 — Energy (Reserved)

BMS → External  |  NOT TRANSMITTED  |  Reserved for future use

This frame is defined in the protocol but not yet transmitted. Capacity (Ah), remaining capacity, cycle count, and State of Health (SOH) are not available in the current firmware version. Displays should show “—” for these fields.

When implemented in a future firmware update, the frame layout will be:

ByteTypeScaleUnitDescription
0–1uint16÷ 10AhTotal capacity
2–3uint16÷ 10AhRemaining capacity
4–5uint16× 1Cycle count
6uint8× 1%State of Health

Frames 0x330–0x337 — Cell Voltages

BMS → External  |  DLC: 4  |  100 ms cycle  |  Only active cells sent
ByteTypeScaleUnitDescription
0–1uint16÷ 1000VCell N voltage (e.g. 3650 = 3.650 V)
2–3uint16÷ 1000VCell N+1 voltage

Cell-to-Frame mapping:

CAN IDCellsCAN IDCells
0x330Cell 1, 20x334Cell 9, 10
0x331Cell 3, 40x335Cell 11, 12
0x332Cell 5, 60x336Cell 13, 14
0x333Cell 7, 80x337Cell 15, 16
ℹ️ Dynamic Cell Count

Frames 0x350–0x351 — Temperature Probes

BMS → External  |  DLC: 8  |  100 ms cycle  |  Only active probes sent

Each frame carries 4 temperature probes as int16 values:

ByteTypeScaleUnitDescription
0–1int16÷ 10°CProbe slot 0 (or 4)
2–3int16÷ 10°CProbe slot 1 (or 5)
4–5int16÷ 10°CProbe slot 2 (or 6)
6–7int16÷ 10°CProbe slot 3 (or 7)
CAN IDProbes
0x350T1, T2, T3, T4
0x351T5, T6, T7, T8

Probe Assignment Order

Temperature slots are populated in a fixed priority order. Absent sensors are skipped — remaining probes shift up.

PrioritySensorCondition
1Internal die temperatureAlways present
2Board temperature sensorOnly on V2/V3 hardware; skipped if unavailable
3Shunt die temperatureOnly when I2C shunt mode is active
4–11External NTC probes 1–8Only if configured (NTC count > 0)
ℹ️ NTC Probes
The BMS supports up to 8 external NTC temperature probes (100K 3950 type). These are available separately and connect via the dedicated temperature sensor connector (Micro-Fit 3.0 — 43025-1000).

Example — 4-probe configuration (Die=32°C, Shunt=19°C, NTC1=18°C, NTC2=19°C):

Frame 0x350 raw bytes:
  Byte 0: 0x40  |  T1 = 320 (int16 LE) -> 32.0°C  (Die Temp)
  Byte 1: 0x01  |
  Byte 2: 0xBE  |  T2 = 190 (int16 LE) -> 19.0°C  (Shunt Temp)
  Byte 3: 0x00  |
  Byte 4: 0xB4  |  T3 = 180 (int16 LE) -> 18.0°C  (NTC_1)
  Byte 5: 0x00  |
  Byte 6: 0xBE  |  T4 = 190 (int16 LE) -> 19.0°C  (NTC_2)
  Byte 7: 0x00  |

Frame 0x360 — I/O State

BMS → External  |  DLC: 2  |  100 ms cycle
ByteTypeDescription
0–1uint16 (LE)I/O bitmask
BitNameMeaning
0CHGCharge contactor (1 = ON, 0 = OFF)
1DSCDischarge contactor (1 = ON, 0 = OFF)
2BALCell balancing active (1 = Active)
3DI1Digital Input 1 — galvanically isolated (1 = active)
4DI2Digital Input 2 — galvanically isolated (1 = active)
5DI3Digital Input 3 — galvanically isolated (1 = active)
6–15Reserved for future expansion
ℹ️ Digital Inputs

Frame 0x370 — Faults & Warnings

BMS → External  |  DLC: 4  |  100 ms cycle
ByteTypeDescription
0–1uint16 (LE)Warning bitmask (informational — yellow indicator)
2–3uint16 (LE)Fault bitmask (critical — red indicator, forces Status = FAULT)

Warning bits:

BitMeaning
0General BMS alarm

Fault bits:

BitMeaning
0Cell overvoltage (cell exceeds OV cutoff)
1Cell undervoltage (cell below UV cutoff)
2Over-temperature (probe exceeds temperature cutoff)
3Emergency power-down

When any fault bit is set, the Status byte in frame 0x300 is forced to 3 (Fault). Displays should show a red fault banner with the hex fault code.

4. CAN Bus Protocol — Command Frame

Frame 0x3A0 — Command (External → BMS)

External → BMS  |  DLC: 5  |  On demand  |  Two-step safety protocol

This is the only frame the BMS accepts from external devices. It uses a two-step ARM + EXECUTE safety protocol to prevent accidental commands from CAN bus noise or software bugs.

ByteTypeDescription
0uint8Command: 0x01 = ARM, 0x02 = EXECUTE
1–4bytesSafety key: { 0x52, 0x53, 0x54, 0x52 } (“RSTR”)

Two-Step Protocol

StepByte 0Action
1. ARM0x01BMS enters armed state. A 2-second execution window opens.
2. EXECUTE0x02If armed and within 2 seconds → BMS soft-restarts. Otherwise → ignored.
🔴 Safety Key Mandatory

Every command frame (both ARM and EXECUTE) MUST include the 4-byte safety key { 0x52, 0x53, 0x54, 0x52 } at bytes 1–4. This prevents:

Frames with an incorrect key are silently rejected.

Protocol Sequence Diagram

 External Device                  BattPulse BMS
  |                                |
  |-- 0x3A0 [01 52 53 54 52] --->|  ARM - armed state, 2s timer starts
  |                                |
  |     (within 2 seconds)         |
  |                                |
  |-- 0x3A0 [02 52 53 54 52] --->|  EXECUTE - BMS restarts!
  |                                |
  |        ~~~ BMS reboots ~~~     |
  |        (2-4 second gap)        |
  |                                |
  |<-- 0x300, 0x330, etc. --------|  BMS back online, CAN TX resumes

Timeout Behaviour

ScenarioResult
ARM → EXECUTE (within 2s)BMS restarts
ARM → (wait > 2s) → EXECUTEIgnored (timer expired)
EXECUTE without prior ARMIgnored
ARM → ARM → EXECUTE (within 2s)BMS restarts (second ARM resets timer)
ARM only — EXECUTE never sentArmed state expires silently after 2s
ARM with wrong key → EXECUTE with correct keyIgnored (ARM was rejected)

Restart Details

5. CAN Bus Protocol — Code Examples

Reading Pack Status from BMS (C / Embedded)

/* Parse a received 0x300 frame from the BattPulse Leader BMS.
 * Adapt to your CAN library and microcontroller platform. */

void parsePackStatus(const uint8_t *data, uint8_t dlc) {
    if (dlc < 7) return;

    uint16_t rawV   = data[0] | (data[1] << 8);   // little-endian
    int16_t  rawI   = data[2] | (data[3] << 8);
    uint16_t rawSOC = data[4] | (data[5] << 8);
    uint8_t  status = data[6];

    float packVoltage = rawV / 100.0;    // Volts
    float packCurrent = rawI / 10.0;     // Amps (+ = charging)
    float soc         = rawSOC / 10.0;   // Percent

    printf("Pack: %.2fV  %.1fA  SOC: %.1f%%  Status: %d\n",
           packVoltage, packCurrent, soc, status);
}

Sending a Restart Command to the BMS (C / Embedded)

/* Send the two-step ARM + EXECUTE restart command to the BMS.
 * Adapt to your CAN driver / controller. */

void sendBmsRestart() {
    can_frame_t cmd;
    memset(&cmd, 0, sizeof(cmd));
    cmd.id  = 0x3A0;
    cmd.dlc = 5;

    // Step 1: ARM
    cmd.data[0] = 0x01;    // ARM restart
    cmd.data[1] = 0x52;    // Safety key: 'R'
    cmd.data[2] = 0x53;    // Safety key: 'S'
    cmd.data[3] = 0x54;    // Safety key: 'T'
    cmd.data[4] = 0x52;    // Safety key: 'R'
    can_send(&cmd);

    delay_ms(200);  // Brief pause between steps

    // Step 2: EXECUTE
    cmd.data[0] = 0x02;    // EXECUTE restart
    can_send(&cmd);

    printf("Restart command sent - BMS will reboot in ~2-4 seconds\n");
}

Reading Cell Voltages (C / Embedded)

/* Parse cell voltage frames 0x330-0x337.
 * Call this for each received cell voltage frame. */

float cellVoltages[16];
int   cellCount = 0;

void parseCellVoltageFrame(uint16_t canId, const uint8_t *data) {
    int offset = canId - 0x330;
    if (offset < 0 || offset > 7) return;

    int cell1 = offset * 2;
    int cell2 = cell1 + 1;

    uint16_t raw1 = data[0] | (data[1] << 8);
    uint16_t raw2 = data[2] | (data[3] << 8);

    cellVoltages[cell1] = raw1 / 1000.0;  // Volts
    if (raw2 > 0) {
        cellVoltages[cell2] = raw2 / 1000.0;
        if (cell2 + 1 > cellCount) cellCount = cell2 + 1;
    } else {
        if (cell1 + 1 > cellCount) cellCount = cell1 + 1;
    }
}

6. WiFi HTTP/JSON API — Overview

The BattPulse Leader BMS runs a built-in web server on port 80. External devices (displays, dashboards, mobile apps) connect to the BMS WiFi network and poll the HTTP API.

ParameterValue
TransportHTTP 1.1
Content-Typetext/plain (request) → JSON (response)
Endpointhttp://<BMS_IP>/JsonHandle
MethodPOST (with JSON body)
ResponseJSON array — [{ ... }]
Port80

The BMS supports these request types via the "type" field in the JSON body:

  1. dashboard — returns pack status, temperatures, I/O states
  2. cellStates — returns individual cell voltages and colours
💡 BattPulse Display 7″ Compatibility
The BattPulse Display 7″ (WiFi mode, BMS type = “BattPulse”) polls the /JsonHandle endpoint automatically, alternating between dashboard and cellStates requests at ~250 ms intervals. Get the BattPulse Display 7″ →

7. WiFi HTTP/JSON API — Endpoints

Dashboard Request

POST /JsonHandle  |  Body: {"type":"dashboard"}

Request:

POST /JsonHandle HTTP/1.1
Content-Type: text/plain

{"type":"dashboard"}

Response:

[{
  "type": "dashboard",
  "status": {
    "current": -12.5,
    "event": "Ready   ",
    "SoC": "%85",
    "PackV": "51.20",
    "MaxC": "3.65",
    "MinC": "3.60"
  },
  "TempProbes": {
    "Die Temp": 32,
    "ShuntTemp": 19,
    "Aux_1": 18,
    "Aux_2": 19
  },
  "IO_States": {
    "IN1": 0,
    "IN2": 0,
    "DO1": 1,
    "DO2": 1,
    "CHG": 1,
    "DSC": 1
  }
}]

Field Reference:

FieldTypeDescription
status.currentfloatPack current in amps (negative = charging, positive = discharging)
status.eventstringLast event message (e.g. “Ready”, “Charging limit on 50”)
status.SoCstringState of charge with % prefix (e.g. “%85”)
status.PackVstringPack voltage as formatted string
status.MaxCstringHighest cell voltage as formatted string
status.MinCstringLowest cell voltage as formatted string
TempProbes.*objectNamed temperature probes — key = sensor name, value = °C (integer)
IO_States.*objectNamed I/O states — key = I/O name, value = 0 (OFF) or 1 (ON)
ℹ️ Dynamic Temperature Probes
The TempProbes object only includes active sensors. Internal sensors that are not present are omitted. External NTC probes appear as Aux_1, Aux_2, etc., based on the configured probe count.

Cell States Request

POST /JsonHandle  |  Body: {"type":"cellStates"}

Request:

POST /JsonHandle HTTP/1.1
Content-Type: text/plain

{"type":"cellStates"}

Response (example: 7S pack):

[{
  "type": "cellStates",
  "cells": {
    "Cell1": 3.650,
    "Cell2": 3.645,
    "Cell3": 3.660,
    "Cell4": 3.640,
    "Cell5": 3.655,
    "Cell6": 3.648,
    "Cell7": 3.652
  },
  "colors": {
    "1": "green",
    "2": "green",
    "3": "green",
    "4": "green",
    "5": "green",
    "6": "green",
    "7": "green"
  },
  "status": {
    "current": -12.5
  },
  "IO_States": {
    "IN1": 0,
    "IN2": 0,
    "DO1": 1,
    "DO2": 1,
    "CHG": 1,
    "DSC": 1
  }
}]

Field Reference:

FieldTypeDescription
cellsobjectCell name → voltage pairs. Cell count = number of keys (6–126).
colorsobjectCell index (1-based) → colour string. Colour-coded by BMS based on OV/UV thresholds.
status.currentfloatPack current (same convention as dashboard)
IO_StatesobjectI/O states (same format as dashboard)

8. WiFi HTTP/JSON API — Code Examples

Polling Dashboard Data (Python)

import requests
import json

BMS_IP = "192.168.1.100"
URL = f"http://{BMS_IP}/JsonHandle"

# Get dashboard data
resp = requests.post(URL, data=json.dumps({"type": "dashboard"}))
data = resp.json()[0]

packV   = float(data["status"]["PackV"])
current = data["status"]["current"]
soc     = data["status"]["SoC"]

print(f"Pack: {packV}V  Current: {current}A  SOC: {soc}")

# Get cell voltages
resp = requests.post(URL, data=json.dumps({"type": "cellStates"}))
cells = resp.json()[0]["cells"]

for name, voltage in cells.items():
    print(f"  {name}: {voltage:.3f}V")

Polling Dashboard Data (C / Embedded HTTPClient)

/* Adapt this to your embedded HTTP client library. */

void pollBmsDashboard() {
    http_client_t http;
    http_init(&http, "http://192.168.1.100/JsonHandle");
    http_set_header(&http, "Content-Type", "text/plain");

    int code = http_post(&http, "{\"type\":\"dashboard\"}");
    if (code == 200) {
        const char* payload = http_get_response(&http);
        // Parse JSON response - first element of array
        // Extract status.current, status.SoC, status.PackV, etc.
        printf("Response: %s\n", payload);
    }
    http_close(&http);
}

9. Data Model Reference

BMS Data Fields

FieldCAN SourceWiFi SourceRange
Pack Voltage0x300 bytes 0–1status.PackV0–120 V
Pack Current0x300 bytes 2–3status.current−500 to +500 A
SOC0x300 bytes 4–5status.SoC0–100%
Status0x300 byte 6status.event0–3 (enum)
Max Cell Voltage0x301 bytes 0–1status.MaxC0–5 V
Min Cell Voltage0x301 bytes 2–3status.MinC0–5 V
Cell Voltages0x330–0x337cells.*6–126 cells (pack-dependent), 0–5 V each
Temperatures0x350–0x351TempProbes.*1–8 probes, −50 to +150 °C
I/O States0x360IO_States.*CHG, DSC, BAL, DI1, DI2, DI3
Faults0x370status.eventOV, UV, OT, POWERDOWN

Physical Limits & Clamping

All values are clamped before CAN transmission to prevent invalid data:

ParameterMinMax
Pack voltage0 V120 V
Cell voltage0 V5.0 V
Current−500 A+500 A
Temperature−50 °C+150 °C
Cell count016
Temperature probes08

10. BattPulse Display Compatibility

The BattPulse Leader BMS is natively compatible with the BattPulse BMS Display 7″. Both CAN and WiFi interfaces work out of the box:

ConnectionSetup RequiredData Rate
CAN Bus Wire CAN-H and CAN-L. Set display baud rate to 500 kbps. Done. 100 ms (10 Hz)
WiFi Connect display to same WiFi. Enter BMS IP. Select “BattPulse” BMS type. Done. ~250 ms (4 Hz)

🛒 Complete BMS + Display Solution

Pair the BattPulse Leader BMS with the BattPulse Display 7″ for a plug-and-play battery monitoring system.

🔋 Get the BMS →     🖥 Get the Display →

11. Troubleshooting

ProblemCAN BusWiFi
No data received from BMS Check CAN-H/CAN-L wiring. Verify 500 kbps baud rate. Check 120 Ω termination at both ends. Confirm transceiver is powered. Verify BMS IP. Check WiFi SSID/password. Confirm BMS is connected to the same network.
Wrong cell count Cell count is set in BMS settings. Only active cell frames are sent. Check BMS configuration. Cell count = number of keys in cells object. Verify cell count setting.
Temperature probes missing Only active probes are transmitted. Check NTC sensor hardware and probe count in settings. Only active probes appear in TempProbes. Absent sensors are auto-skipped.
Restart command ignored Verify safety key bytes exactly match {0x52, 0x53, 0x54, 0x52}. ARM must precede EXECUTE within 2 seconds. N/A — restart is CAN-only.
CAN TX failures Common causes: no bus termination, baud mismatch, wiring fault. N/A
Current reads zero Check current sensor connection. Verify shunt wiring and calibration in settings. Same — current sensor configuration issue.
WiFi JSON returns empty N/A Verify POST body is valid JSON. Check "type" field spelling: must be "dashboard" or "cellStates" (case-sensitive).