BattPulse
Securing the Energy Transformation
BMS Display 7"
Developer API Reference
CAN Bus Protocol
WiFi HTTP/JSON API
Integration guide for BMS manufacturers and developers.
Implement these protocols to make your BMS compatible with the BattPulse Display.
Contents
- Introduction
- Data Model
- CAN Bus Protocol β Overview
- CAN Bus Protocol β Frame Reference
- CAN Bus Protocol β Command Frame
- CAN Bus Protocol β Code Examples
- WiFi HTTP/JSON Protocol β Overview
- WiFi HTTP/JSON Protocol β Endpoints
- WiFi HTTP/JSON Protocol β Code Examples
- Dynamic Hardware Support
- Troubleshooting
1. Introduction
The BattPulse BMS Display 7" is an open-protocol touch display that shows real-time BMS data from any compatible battery management system. It supports two communication methods:
| Method |
Transport |
Speed |
Best For |
| CAN Bus |
CAN 2.0A (11-bit) |
100 ms cycle |
Direct wired connection, vehicles, industrial, no WiFi needed |
| WiFi |
HTTP POST / JSON |
~250 ms cycle |
Remote monitoring, wall-mounted displays, existing WiFi infrastructure |
π Who Is This Document For?
This API reference is for BMS manufacturers and embedded developers who want to make their BMS compatible with the BattPulse Display. Implement either the CAN Bus protocol or the WiFi JSON API (or both) and the display will visualise your BMS data automatically.
2. Data Model
Regardless of the transport (CAN or WiFi), the display maintains the same internal data model:
| Field | Type | Range | Description |
| Pack Voltage | float | 0 β 100 V | Total pack voltage |
| Pack Current | float | -500 β +500 A | Positive = discharge, Negative = charge |
| SOC | float | 0 β 100 % | State of Charge |
| Max Cell | float | 0 β 5 V | Highest individual cell voltage |
| Min Cell | float | 0 β 5 V | Lowest individual cell voltage |
| Cell Count | uint8 | 6 β 16 | Number of series cells (auto-detected) |
| Cell Voltages | float[] | up to 32 | Individual cell voltages in volts |
| Temperature Probes | struct[] | up to 8 | Name + value (Β°C) for each active probe |
| I/O States | struct[] | up to 8 | Name + ON/OFF for each I/O (FETs, balancing, etc.) |
| Event String | string | 32 chars | Status message: "OK", "WARN 0x01", "FAULT 0x03" |
| Status | enum | 0β3 | 0=Idle, 1=Charging, 2=Discharging, 3=Fault |
3. CAN Bus Protocol β Overview
Physical Layer
| Parameter | Value |
| Standard | CAN 2.0A (11-bit identifiers) |
| Baud Rate | 500 kbps (default), 125 / 250 / 1000 kbps supported |
| Byte Order | Little-endian (LSB first) |
| Transceiver | TJA1051T/3 (3.3 V compatible) |
| Termination | 120 Ξ© onboard (jumper selectable) |
| Cycle Time | 100 ms (all frames) |
| Frame Format | Standard (11-bit), no extended, no RTR |
CAN ID Map
| CAN ID(s) |
Direction |
Frame Name |
DLC |
Cycle |
0x300 |
BMS β Display |
Pack Status (V, I, SOC) |
8 |
100 ms |
0x301 |
BMS β Display |
Pack Status Extended (MaxC, MinC, Temp) |
8 |
100 ms |
0x330 β 0x337 |
BMS β Display |
Cell Voltages (2 cells per frame) |
4 |
100 ms |
0x350 β 0x351 |
BMS β Display |
Temperature Probes (4 per frame) |
8 |
100 ms |
0x360 |
BMS β Display |
I/O State (FETs, Balancing, Inputs) |
2 |
100 ms |
0x370 |
BMS β Display |
Fault / Warning Bitmasks |
4 |
100 ms |
0x3A0 |
Display β BMS |
Command Frame (Restart) |
5 |
On demand |
π‘ All CAN IDs are configurable
The base IDs above are defaults. Users can change them in the display's Settings page to match any CAN bus layout.
4. CAN Bus Protocol β Frame Reference
| Byte | Type | Scale | Unit | Description |
| 0β1 | uint16 | Γ· 100 | V | Pack Voltage (e.g. 5120 = 51.20 V) |
| 2β3 | int16 | Γ· 10 | A | Pack Current (+ = discharge, β = charge) |
| 4β5 | uint16 | Γ· 10 | % | SOC (e.g. 850 = 85.0%) |
| 6 | uint8 | β | β | Status: 0=Idle, 1=Charge, 2=Discharge, 3=Fault |
| 7 | uint8 | β | β | Reserved |
| Byte | Type | Scale | Unit | Description |
| 0β1 | uint16 | Γ· 1000 | V | Max Cell Voltage |
| 2β3 | uint16 | Γ· 1000 | V | Min Cell Voltage |
| 4β5 | int16 | Γ· 10 | Β°C | Max Temperature |
| 6β7 | int16 | Γ· 10 | Β°C | Min Temperature |
| Byte | Type | Scale | Unit | Description |
| 0β1 | uint16 | Γ· 1000 | V | Cell N voltage (e.g. 3650 = 3.650 V) |
| 2β3 | uint16 | Γ· 1000 | V | Cell N+1 voltage |
Cell-to-Frame mapping:
| CAN ID | Cells | CAN ID | Cells |
0x330 | Cell 1, 2 | 0x334 | Cell 9, 10 |
0x331 | Cell 3, 4 | 0x335 | Cell 11, 12 |
0x332 | Cell 5, 6 | 0x336 | Cell 13, 14 |
0x333 | Cell 7, 8 | 0x337 | Cell 15, 16 |
βΉοΈ Dynamic Cell Count
- Only send frames for cells that exist. Example: 8S pack β send
0x330β0x333 only.
- For odd cell counts (e.g. 7S), set the last frame's second cell to
0x0000. The display detects this as padding.
- The display auto-detects the cell count from the frames it receives.
Each frame carries 4 temperature probes:
| Byte | Type | Scale | Unit | Description |
| 0β1 | int16 | Γ· 10 | Β°C | Probe slot 0 (or 4) |
| 2β3 | int16 | Γ· 10 | Β°C | Probe slot 1 (or 5) |
| 4β5 | int16 | Γ· 10 | Β°C | Probe slot 2 (or 6) |
| 6β7 | int16 | Γ· 10 | Β°C | Probe slot 3 (or 7) |
| CAN ID | Probes |
0x350 | T1, T2, T3, T4 |
0x351 | T5, T6, T7, T8 |
βΉοΈ Dynamic Probe Count
- Pack probes contiguously from slot 0. Unused slots must be
0x0000.
- The display auto-detects active probes (raw value β 0) and hides unused slots.
- If you have 2 probes, only frame
0x350 is needed (bytes 4β7 = 0x0000).
| Byte | Type | Description |
| 0β1 | uint16 (LE) | I/O bitmask |
| Bit | Name on Display | Meaning |
| 0 | CHG | Charge FET (1 = ON, 0 = OFF) |
| 1 | DSC | Discharge FET (1 = ON) |
| 2 | BAL | Balancing active (1 = ON) |
| 3 | IN1 | Digital input 1 (1 = ON) |
| 4 | IN2 | Digital input 2 (1 = ON) |
| 5 | IN3 | Digital input 3 (1 = ON) |
| 6β15 | IN4βIN13 | Reserved for future inputs |
βΉοΈ Dynamic Inputs
- Bits 0β2 (CHG, DSC, BAL) are always shown on the display regardless of state.
- Bits 3+ are dynamic β the display auto-detects which input bits are set and shows them as IN1, IN2, IN3, etc.
- Only set the bits your BMS hardware actually supports. Inputs whose bit is 0 are hidden.
- Up to 8 total I/O states are displayed (3 fixed + up to 5 dynamic inputs).
| Byte | Type | Description |
| 0β1 | uint16 (LE) | Warning bitmask (yellow indicator) |
| 2β3 | uint16 (LE) | Fault bitmask (red indicator) |
Warning bits:
| Bit | Meaning |
| 0 | General BMS alarm |
Fault bits:
| Bit | Meaning |
| 0 | Cell overvoltage |
| 1 | Cell undervoltage |
| 2 | Over-temperature |
| 3 | Emergency power-down |
When any fault bit is set, the display shows a red fault banner with the fault code. Warning bits trigger a yellow event indicator.
5. CAN Bus Protocol β Command Frame
This is the only frame sent from the display to the BMS. It uses a two-step ARM + EXECUTE safety protocol to prevent accidental commands.
| Byte | Type | Description |
| 0 | uint8 | Command: 0x01 = ARM, 0x02 = EXECUTE |
| 1β4 | bytes | Safety key: { 0x52, 0x53, 0x54, 0x52 } ("RSTR") |
Two-Step Protocol
| Step | Byte 0 | Action |
| 1. ARM | 0x01 | BMS enters armed state, starts 2-second timer |
| 2. EXECUTE | 0x02 | If armed and within 2 seconds β BMS restarts |
β οΈ Safety Key Required
Every command frame MUST include the 4-byte safety key
{ 0x52, 0x53, 0x54, 0x52 } at bytes 1β4. Frames with incorrect keys are silently rejected by the BMS.
Protocol Sequence Diagram
Display BMS
β β
βββ 0x3A0 [01 52 53 54 52] ββββΊβ ARM β armed state, 2s timer
β β
β (within 2 seconds) β
β β
βββ 0x3A0 [02 52 53 54 52] ββββΊβ EXECUTE β restart!
β β
β ~~~ BMS reboots ~~~ β
β (2β4 second gap) β
β β
ββββ 0x300, 0x330, etc. βββββββββ BMS back online
Timeout Behaviour
| Scenario | Result |
| ARM β EXECUTE (within 2s) | BMS restarts |
| ARM β (wait > 2s) β EXECUTE | Ignored (timer expired) |
| EXECUTE without ARM | Ignored |
| ARM β ARM β EXECUTE | BMS restarts (second ARM resets timer) |
| ARM only (no EXECUTE) | Armed state expires silently after 2s |
6. CAN Bus Protocol β Code Examples
Sending BMS Data to the Display (C)
void sendPackStatus(float packVoltage, float packCurrent,
float soc, uint8_t status) {
uint8_t data[8] = {0};
uint16_t v = (uint16_t)(packVoltage * 100);
int16_t i = (int16_t)(packCurrent * 10);
uint16_t s = (uint16_t)(soc * 10);
data[0] = v & 0xFF; data[1] = v >> 8;
data[2] = i & 0xFF; data[3] = i >> 8;
data[4] = s & 0xFF; data[5] = s >> 8;
data[6] = status;
data[7] = 0;
can_send(0x300, data, 8);
}
void sendCellVoltages(float *cellV, int cellCount) {
for (int pair = 0; pair < (cellCount + 1) / 2; pair++) {
uint8_t data[4] = {0};
int c0 = pair * 2;
int c1 = c0 + 1;
uint16_t v0 = (uint16_t)(cellV[c0] * 1000);
uint16_t v1 = (c1 < cellCount)
? (uint16_t)(cellV[c1] * 1000) : 0;
data[0] = v0 & 0xFF; data[1] = v0 >> 8;
data[2] = v1 & 0xFF; data[3] = v1 >> 8;
can_send(0x330 + pair, data, 4);
}
}
Handling the Restart Command on the BMS (C)
static bool restart_armed = false;
static uint32_t arm_time_ms = 0;
void handleCommandFrame(const uint8_t *data, uint8_t dlc) {
if (dlc < 5) return;
if (data[1] != 0x52 || data[2] != 0x53 ||
data[3] != 0x54 || data[4] != 0x52) return;
switch (data[0]) {
case 0x01:
restart_armed = true;
arm_time_ms = get_tick_ms();
break;
case 0x02:
if (restart_armed &&
(get_tick_ms() - arm_time_ms) < 2000) {
system_restart();
}
break;
}
}
7. WiFi HTTP/JSON Protocol β Overview
The display connects to the BMS over WiFi using HTTP POST requests to a single endpoint. The BMS acts as an HTTP server.
| Parameter | Value |
| Transport | HTTP 1.1 POST |
| Content-Type | text/plain |
| Endpoint | http://<BMS_IP>/JsonHandle |
| Request Body | JSON object with "type" field |
| Response | JSON object or array |
| Poll Interval | ~250 ms (alternating dashboard / cellStates) |
The display sends two types of requests in alternation:
- Dashboard request β returns pack status, temperatures, I/O states
- Cell States request β returns individual cell voltages and colours
8. WiFi HTTP/JSON Protocol β Endpoints
Request:
POST /JsonHandle HTTP/1.1
Content-Type: text/plain
{"type":"dash"}
Response (BattPulse format):
{
"status": {
"current": -12.5,
"SoC": 85.0,
"totSystV": 51.20,
"MaxC": 3.65,
"MinC": 3.60,
"Balance": 1,
"event": "OK"
},
"temps": {
"ChipTemp": 32.0,
"ShuntTemp": 19.0,
"Aux_1": 18.0,
"Aux_2": 19.0
},
"ntcs": {
"NTC_1": 22.5,
"NTC_2": 23.0
},
"IO_States": {
"CHG": 1,
"DSC": 1,
"BAL": 0
}
}
βΉοΈ Temperature Names Are Displayed
When data comes via WiFi, the JSON key names (e.g. "ChipTemp", "ShuntTemp", "Aux_1") are displayed directly on the dashboard as probe labels. Choose meaningful names for your sensors.
Field Reference:
| Field | Type | Description |
status.current | float | Pack current in amps (+ = discharge, β = charge) |
status.SoC | float | State of charge (0β100) |
status.totSystV | float | Total pack voltage |
status.MaxC | float | Highest cell voltage |
status.MinC | float | Lowest cell voltage |
status.Balance | 0/1 | Balancing active flag |
status.event | string | Event / status message |
temps.* | object | Named temperature probes (key = name, value = Β°C) |
ntcs.* | object | Named NTC probes (merged with temps on display) |
IO_States.* | object | Named I/O states (key = name, value = 0 or 1) |
Request:
POST /JsonHandle HTTP/1.1
Content-Type: text/plain
{"type":"cellStates"}
Response:
{
"cells": {
"Cell1": 3.650,
"Cell2": 3.645,
"Cell3": 3.660,
"Cell4": 3.640,
"Cell5": 3.655,
"Cell6": 3.648,
"Cell7": 3.652
},
"colors": {
"1": "#30B32D",
"2": "#30B32D",
"3": "#F5A623",
"4": "#30B32D"
},
"status": {
"current": -12.5
},
"IO_States": {
"CHG": 1,
"DSC": 1
}
}
Field Reference:
| Field | Type | Description |
cells | object | Key-value pairs of cell names and voltages. Cell count = number of keys. |
colors | object | Optional. Key = cell number (1-based), value = hex colour for that cell's border. |
status.current | float | Pack current (same as dashboard) |
IO_States | object | I/O states (same format as dashboard) |
π‘ Cell Colours
The
colors object is optional. If provided, the display uses your BMS-defined colours for each cell border. If not provided, the display uses its own colour coding based on voltage ranges (green = good, yellow = warning, red = critical).
9. WiFi HTTP/JSON Protocol β Code Examples
Minimal BMS HTTP Server (C β Pseudocode)
void handleJsonRequest(const char *body, http_response_t *resp) {
const char *type = json_get_string(body, "type");
if (strcmp(type, "dash") == 0) {
http_send_json(resp,
"{"
" \"status\": {"
" \"current\": -12.5,"
" \"SoC\": 85.0,"
" \"totSystV\": 51.20,"
" \"MaxC\": 3.65,"
" \"MinC\": 3.60,"
" \"Balance\": 1,"
" \"event\": \"OK\""
" },"
" \"temps\": { \"BoardTemp\": 32.0, \"Shunt\": 19.0 },"
" \"IO_States\": { \"CHG\": 1, \"DSC\": 1, \"BAL\": 0 }"
"}"
);
}
else if (strcmp(type, "cellStates") == 0) {
http_send_json(resp,
"{"
" \"cells\": {"
" \"Cell1\": 3.650, \"Cell2\": 3.645,"
" \"Cell3\": 3.660, \"Cell4\": 3.640,"
" \"Cell5\": 3.655, \"Cell6\": 3.648,"
" \"Cell7\": 3.652"
" }"
"}"
);
}
}
10. Dynamic Hardware Support
The display is designed to work with many different BMS configurations without needing firmware changes. Here's what adapts automatically:
| Feature |
CAN Bus Behaviour |
WiFi Behaviour |
| Cell Count |
Auto-detected from cell voltage frames received. 6β16S. Odd counts supported (padding = 0x0000). |
Auto-detected from number of keys in cells object. |
| Temp Probe Count |
Auto-detected from non-zero slots in temp frames. 1β8 probes. Labels: T1, T2, ... T8. |
Auto-detected from temps + ntcs keys. Labels from JSON key names. |
| Temp Probe Names |
Generic labels: T1, T2, T3, etc. (CAN protocol doesn't carry names.) |
Uses sensor names from JSON (e.g. "ChipTemp", "ShuntTemp", "Aux_1"). |
| I/O States |
CHG, DSC, BAL always shown; IN1βINx auto-detected from bits 3+. |
Dynamic: any number of named I/O states from JSON. |
| Cell Colours |
Auto-generated from voltage ranges. |
BMS can supply custom colours via colors object, or auto-generated. |
11. Troubleshooting
| Problem | CAN Bus | WiFi |
| No data on display |
Check CAN-H/CAN-L wiring. Verify baud rate matches. Check termination. |
Verify BMS IP. Check WiFi connection. Try "Scan Network". |
| Wrong cell count |
Ensure only active cell frames are sent. Use 0x0000 padding for odd counts. |
Check cells object has correct number of entries. |
| Extra 0 Β°C temp probes |
Set unused probe slots to 0x0000 (raw int16 = 0). Display auto-hides them. |
Only include active probes in temps/ntcs objects. |
| Restart command ignored |
Verify safety key bytes match exactly. ARM must precede EXECUTE within 2s. |
N/A (no restart via WiFi) |
| Slow update rate |
Ensure 100ms cycle. Check CAN bus load. |
BMS HTTP server must respond within 900ms. Keep JSON compact. |