← Back to: Battery Charging / Gauging / Protection / BMS
Executive Summary
Goal: Build a cross-brand, reusable Pack NVM closed loop in the charging domain: factory calibration → schema-based persistence → versioning → charger/JEITA readable policy.
Scope: Excludes FET/insulation. Defines fields, flows, and hooks to the charging state machine and JEITA zones.
Method: Schema first (mapping + graceful downgrade required). OTP holds immutable traceability/keys; EEPROM/Flash holds updateable counters, LUTs, and logs.
Schema First
Unify the language of interchangeability. Mapping and downgrade strategies are validated against a single schema.
Clear Storage Roles
OTP = immutable identity and keys. EEPROM/Flash = updateable model, counters, and ring-buffer logs.
Minimum Viable Pack NVM Schema
Purpose: Provide a minimal, interchangeable field set so Brand A ↔ Brand B substitutions preserve meaning. Fields are grouped, versioned, and partitioned into OTP vs EEPROM/Flash for traceability and in-field updates.
- OTP (P0): PACK_PN, MFR, DATE_CODE, SERIAL, KEY_ID, KEY_INJECT_TS, NVM_SCHEMA_VER (initial shadow), TRACE_*.
- EEPROM/Flash (P1–P3): Models, counters, logs, CAL_VER, MIGRATION_FLAG.
- OCV_LUT: 17×3 grid @ mV (SoC 0–100% step 6%; temps 0/25/45 °C); monotonic & bounds check.
- Formats: R0 = Q8.8 mΩ; Capacity_Ah_ref = Q8.8; Energy_Wh_Acc = Q16.16; timestamps = UNIX seconds (UTC).
| Name | Group | Type / Format | Unit / Range / Notes | Access | Page |
|---|---|---|---|---|---|
| PACK_PN | Identity | string(24) | ASCII; trim/pad 0x00 | R | P0 |
| SERIAL | Identity | string(16) | ASCII; unique per pack | R | P0 |
| MFR | Identity | string(16) | Manufacturer code | R | P0 |
| DATE_CODE | Identity | yyyww | Year+ISO week | R | P0 |
| CELLS_CONFIG | Identity | uint8 | 2..6 (series cells) | R | P0 |
| CELL_VENDOR[6] | Identity | enum | Per-cell vendor map | R | P0 |
| OCV_LUT[v,temp] | Model | grid 17×3 @ mV | SoC 0–100% step 6%; 0/25/45 °C | RW | P2 |
| OCV_LUT_VER | Model | uint8 | Model version | RW | P2 |
| R0 | Model | Q8.8 | mΩ | RW | P2 |
| Tau[2] | Model | uint16×2 | Dual time constants | RW | P2 |
| Capacity_Ah_ref | Model | Q8.8 | Ah reference | RW | P2 |
| Impedance_BurnIn.AC_1kHz | Model | Q8.8 | AC @ 1 kHz | RW | P2 |
| Impedance_BurnIn.DC_10s | Model | mΩ | DC @ 10 s | RW | P2 |
| ThermalCoeffs.dV_dT | Model | int16 | μV/°C | RW | P2 |
| ThermalCoeffs.dR_dT | Model | int32 | ppm/°C | RW | P2 |
| Cycle_Total | Lifetime | uint32 | ΔDoD ≥ 80% counts 1 | RW | P1 |
| Cycle_EQ_1C | Lifetime | uint32 | Equivalent 1C cycles | RW | P1 |
| Time_Hours | Lifetime | uint32 | Total runtime hours | RW | P1 |
| HighTemp_Hours | Lifetime | uint32 | > 45 °C | RW | P1 |
| LowTemp_Hours | Lifetime | uint32 | < 0 °C | RW | P1 |
| FastCharge_Count | Lifetime | uint16 | I ≥ 0.8C for > 5 min | RW | P1 |
| Coulomb_Signed_Base | Signatures | int64 | HMAC scope anchor | RW | P2 |
| Energy_Wh_Acc | Signatures | Q16.16 | Accumulated energy | RW | P2 |
| Last_Cal_TS | Signatures | unix | UTC seconds | RW | P2 |
| KEY_ID | Traceability | uint16 | HSM key slot | WO-at-line | P0 |
| KEY_INJECT_TS | Traceability | unix | UTC seconds | WO-at-line | P0 |
| TRACE_LOT | Traceability | string(12) | Lot identifier | R | P0 |
| TRACE_STATION | Traceability | string(8) | Station code | WO-at-line | P0 |
| Last_Trigger | Logs | enum | Wake/Ship/OT/UV/OC | RW | P3 |
| Trigger_Counts[8] | Logs | uint16×8 | Per-event counters | RW | P3 |
| NVM_SCHEMA_VER | Version | uint8 | Schema version (shadow in P0) | WO-at-line | P0/P2 |
| CAL_VER | Version | uint8 | Calibration version | RW | P2 |
| MIGRATION_FLAG | Version | bit | Set on cross-brand write | RW | P3 |
NVM Partitioning & Version Control
Goal: Define OTP vs EEPROM/Flash roles, page layout (Page0..3), CRC/signature policy, and rollback safety.
Compatibility: Read lower versions with defaults; read higher versions in read-only or via migration; minor changes remain backward compatible.
Validation order: Page CRC → optional package hash → signature verification (if enabled).
Page roles & access
- Page0 (OTP): Identity, keys, traceability. Read-only in field.
- Page1 (Life): Lifetime counters; updateable in field.
- Page2 (Model): OCV/R0/Thermal; updateable with version checks.
- Page3 (Logs): Fixed-size entries, ring buffer, wear leveling.
Headers, CRC & signatures
- PAGE_HDR: MAGIC “PNVM”, PAGE_ID, PAGE_VER, PAGE_LEN, PAGE_FLAGS, PAGE_CRC.
- CRC: CRC-16/CCITT-FALSE (0x1021, init 0xFFFF) or CRC-32/ISO-HDLC.
- Signature (opt.): HMAC-SHA256 over metering baseline and Last_Cal_TS.
Compatibility matrix
- Lower version: backward compatible with defaults.
- Higher version: read-only or migrate with
MIGRATION_FLAG. - Minor changes: compatible if field meaning is preserved.
Wear leveling & rollback
- Logs: fixed entry size, batched writes (e.g., 8 per flush), dual-page flip on Flash.
- Under-voltage write is blocked; failure triggers mirror-page rollback.
- Trigger rollback: CRC/signature error, high-version write attempt, migration abort, power loss.
| Page | Purpose | CRC | Access | Notes |
|---|---|---|---|---|
| P0 (OTP) | Identity / Keys | CRC16 | R | Read-only in field; schema shadow. |
| P1 (Life) | Counters | CRC16 | RW | Monotonic counters; under-voltage guard. |
| P2 (Model) | OCV/R0/Thermal | CRC32 | RW | Version gating; hash/sign optional. |
| P3 (Logs) | Ring buffer | CRC16 | RW | Fixed-length entries; batched flush. |
Factory Line Flow
Objective: Close the loop from write → verify → audit → stock-in → assembly → first-charge learning → in-field updates.
Acceptance: Full readback with page CRC, schema checks (LUT shape/range/monotonic), and signature verification. Export acceptance-report.json for traceability.
Write
- Program Page0..Page3 by
NVM_SCHEMA_VER. - Block writes in under-voltage conditions.
- Compute page CRC immediately after write.
Verify
- Full readback → per-page CRC.
- OCV_LUT shape/range/monotonic checks; fixed-point bounds.
- Signature verification (if enabled).
Audit
- Sample
Impedance_BurnInandCapacity_Ah_refwithin tolerance. - Endianness self-check using a temporary known pattern.
Seal
- Write
TRACE_STATIONandKEY_INJECT_TS. - Freeze
CAL_VERand shadowNVM_SCHEMA_VERin OTP.
Assembly
- After system assembly, first-charge learning writes
Last_Cal_TS. - Mark
MIGRATION_FLAGif a substitution occurred.
In-field
- Update Life and Logs; ring buffer flush in batches (e.g., 8 entries).
- On power loss, rollback to mirror page safely.
Acceptance & Archiving
- Reject units on CRC failure, LUT violations, signature errors, or endianness mismatch.
- Export
acceptance-report.jsonincluding SN, page CRC states, LUT shape/range, ring-buffer cursor checks, and signature status. - Archive reports with lot/traceability for stock-in.
Cross-Brand Migration & Field Mapping
Goal: Preserve meaning when Brand A ↔ Brand B parts are substituted, with explicit downgrade rules and a rollback snapshot.
Hooks: Set MIGRATION_FLAG=1, store a schema snapshot, and update the Telemetry Mapper in the cloud before the line is allowed to switch.
Required artifact: mapping.csv covering every field (A.field → B.field) with a policy of default, locked, or cloud-infer.
Required artifacts
mapping.csvwith full coverage (A.field → B.field).- Declare missing fields as default, locked, or cloud-infer.
- Enumerations and unit scales included in the file.
Version hooks
- Set
MIGRATION_FLAG=1and store the schema snapshot. - Higher versions read-only or migrate via script.
- Minor versions remain backward compatible.
Rollback policy
- Keep a mirror image of original pages (with header+CRC).
- Trigger rollback on CRC/signature error, migration abort, or power loss.
Cloud gate
- Update the Telemetry Mapper first; only then allow line writes.
- SN-based allowlist for staged rollout and A/B checks.
| src_field | dst_field | rule | default_value | enum_map | unit_scale | precision_loss | page_src | page_dst | nullable | null_sentinel | comments |
|---|---|---|---|---|---|---|---|---|---|---|---|
| A.OCV_LUT_21x4 | B.OCV_LUT_17x3 | cloud-infer | – | – | mV:1:1 | curve-fit | P2 | P2 | F | – | Downsample & regress |
| A.KEY_ID | B.KEY_ID | locked | – | – | – | none | P0 | P0 | F | 0x0000 | Traceability only |
| A.Trigger_Counts[8] | B.Trigger_Counts[8] | default | 0 | – | – | none | P3 | P3 | T | 0xFFFF | Initialize if absent |
Security: Key Injection, Signatures & Traceability
Objective: Inject keys on the line and sign metering baselines so energy/coulomb data and traceability are trustworthy.
Policy: KEY_ID lives in OTP; sign Coulomb_Signed_Base and Energy_Wh_Acc (including Last_Cal_TS and a monotonic counter) with HMAC-SHA256.
Access: Chargers/gateways read traceability and verify signatures; writes are restricted to station scripts.
Key injection
KEY_IDin OTP withKEY_INJECT_TSfor traceability.- Key rotation by lot/station using KDF labels.
- Writes only from authorized station scripts.
Signature scope
- HMAC-SHA256 over
SN,Coulomb_Signed_Base,Energy_Wh_Acc,Last_Cal_TS, and a monotonic counter. - Signature fields are read-only outside the line.
Verification path
- Charger/gateway reads traceability and verifies signature.
- Cloud re-verifies and raises alerts on mismatch.
Read policy
- Runtime exposure limited to traceability and verification status.
- No key material is readable in the field.
Audit & forensics
- On failure, isolate the unit and lock writes.
- Log
SN/TS/Stationwith failure type and signature digest. - Optional offline whitelist with limits (count / expiry).
Acceptance checklist
- HSM online and licensed;
KEY_IDmatches lot/station. - Verification pass rate = 100%; failure units quarantined.
- Telemetry includes
{sn, verify_status, last_cal_ts, monotonic, signature_digest}.
Lifetime Counters & Trigger Logs
Goal: Standardize counting rules and trigger logging so fleet statistics are comparable and auditable.
Counters: Cycle_Total (ΔDoD ≥ 80% → +1), Cycle_EQ_1C (∑|ΔQ|/Qref), FastCharge_Count (I ≥ 0.8C for > 5 min), environmental hours aggregated by temperature zones.
Logs: Append-only ring buffer of Trigger{type, ts, vbat, temp, reason}; summary exposed via Last_Trigger and Trigger_Counts{}; protected by page CRC and rollback on fault.
Counter definitions
- Cycle_Total: Increment when cumulative ΔDoD ≥ 80% (across sessions).
- Cycle_EQ_1C: Sum(|ΔQ|)/Capacity_Ah_ref (Q16.16 acceptable).
- FastCharge_Count: I ≥ 0.8C for > 5 min (debounce window ≥ 10 min).
- Env hours: Aggregate by zones; persist Hot & Cold hours.
Trigger records
Trigger{ type, ts, vbat, temp, reason }, fixed-size entries.- Append-only ring with batched flush (e.g., 8 entries per write).
- Update
Last_TriggerandTrigger_Counts[i]after commit.
Integrity & rollback
- Per-page CRC required; optional entry CRC16.
- Rollback on CRC/signature error or power-loss mid-write.
Acceptance checks
- Cursor consistency, monotonic timestamps, expected entry count.
- Summary equals log-derived counts; export in acceptance report.
| Name | Definition | Threshold | Debounce | Page |
|---|---|---|---|---|
| Cycle_Total | Cumulative ΔDoD event | ≥ 80% | n/a | P1 |
| Cycle_EQ_1C | ∑|ΔQ| / Capacity_Ah_ref | — | — | P1 |
| FastCharge_Count | High-current charge episode | I ≥ 0.8C & > 5 min | ≥ 10 min | P1 |
| Last_Trigger | Latest event type | Wake/Ship/OT/UV/OC | — | P3 |
Charging-Domain Hooks
Goal: Wire Pack NVM fields into charging policy: JEITA zoning, termination/recharge, and power-path hints.
Readable by charger: OCV_LUT, R0, ThermalCoeffs, Capacity_Ah_ref, Last_Trigger.
Exposure: Report Charger State and JEITA Zone to the edge gateway with timestamped metrics.
Charger-readable
OCV_LUT,R0,ThermalCoeffs,Capacity_Ah_ref,Last_Trigger.- Read-only access; no runtime writes to models.
Decisions
- Cold start/storage wake current limits.
- JEITA derating by temperature zone.
- Recharge thresholds adapt to capacity fade.
Exposure
- Edge report: Charger State + JEITA Zone with metrics.
- Faults attach
Last_Triggerand recent log summary.
Compatibility
- Lower versions use default tables.
- Higher versions are read-only or gated by migration.
Small-Batch Procurement & BOM Remarks
Goal: Force critical requirements into the BOM so substitutions don’t break charging-domain telemetry and traceability.
Must-have: NVM schema version noted in BOM, a submitted mapping.csv (A→B fields) with downgrade policy, and signed safety metering capability.
Guardrails: Update the cloud Telemetry Mapper before switching vendors; do not accept parts lacking NTC/JEITA temperature zoning.
Copy into your BOM (1)
- NVM schema vX required; cross-brand substitutions must submit field mapping & downgrade policy.
- Signed safety metering is mandatory; ICs without telemetry/reporting are not accepted.
Copy into your BOM (2)
- Update cloud Telemetry Mapper before any vendor switch.
- Do not substitute parts lacking NTC / JEITA-compliant temperature zoning.
Analog Devices / Maxim
- MAX17320 – fuel gauge + protection
- LTC4015 – multi-cell buck charger with telemetry
- LTC2949 – high-accuracy energy monitor
Microchip
- MCP73871 – single-cell charger, power-path, JEITA hooks
- PAC1934 – 4-channel power/energy monitor
onsemi
- LC709203F – fuel gauge with NTC support
- NCP1852 – single-cell charger with thermal regulation
Renesas (Intersil)
- ISL94202 – 3–8S BMS AFE with metering interface
- RAA489204 – NVDC buck-boost charger, JEITA, telemetry
ROHM
- BD99954GW – single-cell switching charger, JEITA
- BD99988GW – multi-function charger, system priority
STMicroelectronics
- STC3115 – fuel gauge with temp compensation
- STUSB4500 – USB-C sink PD coordinator (policy hook)
Texas Instruments
- BQ40Z50-R2 – pack fuel gauge, SMBus, life counters
- BQ76952 – multi-cell AFE monitor
- BQ25895 – single-cell switching charger, JEITA
| Vendor / Part | Interface | JEITA/NTC | Signed Metering Hook | Telemetry (V/I/Q/T) | Suggested NVM Pages | Cloud-infer? |
|---|---|---|---|---|---|---|
| ADI LTC4015 | I²C | ✅ | ⚠️ via host | V/I/T | P1/P2 | No |
| ADI MAX17320 | SMBus | ✅ | ✅ | V/I/Q/T | P1/P2/P3 | No |
| Microchip MCP73871 | I²C | ✅ | ⚠️ via host | V/T | P2 | Maybe |
| onsemi LC709203F | I²C | ✅ | ✅ | V/Q/T | P1/P2 | No |
| Renesas RAA489204 | SMBus | ✅ | ⚠️ via host | V/I/T | P1/P2 | No |
| ROHM BD99954GW | I²C | ✅ | ⚠️ via host | V/I/T | P2 | Maybe |
| ST STC3115 | I²C | ✅ | ✅ | V/Q/T | P1/P2 | No |
| TI BQ40Z50-R2 | SMBus | ✅ | ✅ | V/I/Q/T | P1/P2/P3 | No |
Risk notes
- High version/field gaps → read-only or downgrade policy; substitutions must pass cloud mapping review.
- No JEITA or no NTC → reject.
- No
mapping.csvor cloud mapper not updated → reject.
Test & Acceptance Checklist
Objective: Execute a repeatable flow: connect → full readback → CRC → optional hash → signature → model checks → trigger write/read → archive.
Report: Export acceptance-report.json with page states, model checks, and a short trigger summary for each unit SN.
Connection & integrity
- Full readback after connect; verify page sizes and versions.
- Validate CRC16/CRC32, optional SHA-256, and HMAC signature (if enabled).
Model checks
- OCV_LUT shape = 17×3; cell values within mV range; monotonic vs. SoC.
- Reasonable
R0,Tau, andThermalCoeffswindows.
Consistency & logs
- Sample
Impedance_BurnInandCapacity_Ah_refagainst Δ thresholds. - Write 3 triggers (Wake/Ship/OT), read back ring cursors; sync
Last_Trigger&Trigger_Counts{}.
Archiving
- Export
acceptance-report.jsonand store with SN and lot traceability.
Planned acceptance-report.json keys
{
"sn": "...", "schema_ver": 1,
"pages": { "p0":{"ver":1,"crc":"ok"}, "p1":{...}, "p2":{...}, "p3":{...} },
"hash_sha256": "…", "sign_status": "ok|fail|n/a",
"model_check": { "ocv_lut":{"shape":"17x3","range_mV":[...],"monotonic":"ok"}, "r0_milliohm": "...", "tau": [...] },
"consistency": { "impedance_burnin_delta":"%", "capacity_ref_delta":"%" },
"triggers": { "written":["Wake","Ship","OT"], "last":"OT", "counts":[...] },
"ts": "iso8601", "station": "..."
}
Reject criteria
- Any page CRC failure or signature failure.
- OCV_LUT dimension/range violation or non-monotonic behavior.
- Δ thresholds exceeded on impedance/capacity checks.
- Trigger write/read mismatch or ring cursor inconsistency.
FAQ
How do I structure a minimal Pack NVM so cross-brand parts remain interchangeable?
Use a schema first layout with six blocks: identity, model, lifetime, signatures, traceability, and logs, stored across versioned pages P0 to P3. Keep immutable traceability and key identifiers in OTP, and put updateable models, counters, and logs in EEPROM or Flash. Cross brand swaps require mapping.csv plus downgrade rules and a migration snapshot with rollback safety.
Which fields must live in OTP versus EEPROM for traceability and updates?
Place part number, serial, manufacturer, date code, key identifier, key injection time, initial schema version, and trace lot or station in OTP for immutable traceability. Store updateable items in EEPROM or Flash: OCV LUT and model parameters, lifetime counters, trigger logs, calibration version, and the migration flag. This clean split keeps audit trails while allowing safe in field updates.
What is the safest way to store burn-in impedance and capacity without breaking older firmware?
Persist burn in impedance and reference capacity in fixed point formats with documented ranges and units. Guard writes with page version headers and CRC, and validate ranges before commit. Older firmware reads previous versions; newer firmware migrates by downgrade policy or cloud infer when precision changes. Avoid schema free blobs so compatibility and auditing remain predictable across vendors.
How are lifetime counters defined so fast-charge events don’t skew cycle counts?
Count Cycle_Total when cumulative depth of discharge meets or exceeds eighty percent, possibly across sessions. Track Cycle_EQ_1C as the sum of absolute charge throughput divided by Capacity_Ah_ref. Increment FastCharge_Count only when current is at least zero point eight C for longer than five minutes with a ten minute debounce. Aggregate environmental hours by temperature zones for comparable analytics.
How do I sign metering baselines so cloud analytics can trust the data?
Sign a digest produced by HMAC SHA256 over serial number, Coulomb_Signed_Base, Energy_Wh_Acc, Last_Cal_TS, and a monotonic counter. Keep KEY_ID and injection time in OTP, and run signing in a line HSM or trusted execution environment. At runtime the charger or gateway verifies the signature and reports status; no key material is readable in the field.
What migration steps prevent field loss when switching vendors mid-program?
Require mapping.csv that fully covers A to B fields and declares default, locked, or cloud infer behavior. Set MIGRATION_FLAG and store a read only schema snapshot. Update the cloud telemetry mapper before allowing line writes. Keep a mirror of original pages for rollback on CRC or signature failure. Acceptance reports must summarize mapping coverage and any downgraded fields.
How should I log which trigger woke the device after deep storage or shipping?
Append Trigger entries with type, timestamp, battery voltage, temperature, and reason into a fixed size ring buffer with batched flush. After each commit update Last_Trigger and the appropriate Trigger_Counts index. Protect pages with CRC and roll back on interrupted writes. Enumerations should include Wake, Ship, over temperature, under voltage, and over current for consistent fleet level analysis.
Can I reuse main battery NTC data to qualify calibration temperature windows?
Yes, you can gate calibration using the main battery NTC as a temperature window, but record the source and expected accuracy in the model. Keep JEITA zoning separate from calibration windows to avoid policy confusion. If the NTC accuracy or placement is uncertain, prefer conservative limits and mark fields for cloud infer rather than overwriting trusted baselines.
How do I gate first-charge learning so half-baked NVM doesn’t reach customers?
Run first charge learning only after assembly checks pass and required model fields exist. On completion write Last_Cal_TS and lock CAL_VER. If mandatory data are missing, reject the unit or keep models read only until acceptance succeeds. Attach a short trigger exercise and verify summaries so the pack leaves the line with auditable, reproducible calibration signals.
What happens if OCV LUT dimensions change across firmware or vendors?
Treat smaller target grids as backward compatible and read directly. For larger or different grids, refuse writes or migrate by sampling and regression into the standard seventeen by three layout with range and monotonic checks. Record OCV_LUT version, clamp units in millivolts, and track precision loss so analytics can distinguish true aging effects from representation changes.
Which BOM remarks stop purchasing from picking ICs without reporting/signatures?
Add four hard gates to the BOM: NVM schema version required; cross brand substitutions must submit field mapping and a downgrade policy; signed safety metering is mandatory and devices without telemetry are not accepted; update the cloud telemetry mapper before any vendor switch; and do not substitute parts lacking NTC or JEITA compliant temperature zoning for charging policy decisions.
What acceptance tests prove NVM is written, versioned, and readable at line rate?
Execute a repeatable flow: connect the device, perform full readback, validate page CRC, optionally compute a global hash, verify signatures if enabled, and check model dimensions and ranges. Write and read three triggers to exercise the ring buffer. Export acceptance-report.json with page states and summaries, archive by serial number, and quarantine any failing units.