Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions documentation/CAN_Message_Packing.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,54 @@ CRC byte.
| 7 bit4 | Positive contactor can open | bool | `0=no`, `1=yes` |
| 7 bit7:5 | reserved | | transmit `0` |


## Extra section: shunt + HV monitor telemetry (`0x602`-`0x605`, 10 Hz)

These frames mirror all currently available shunt and HV monitor runtime telemetry
(`param::*`) in the same style as contactor telemetry: fixed CAN IDs and raw
telemetry payload bytes without CRC/counter fields. All numeric values are packed
as `uint16` with defined scale/offset.
Values that exceed the representable range are saturated to `0..65535`.

### `BMS_SHUNT_HV_META` (0x602)

| Byte | Signal | Type | Encoding |
|-----|--------|------|----------|
| 0 | Shunt state | enum (`uint8`) | `ShuntState` (`0=INIT,1=OPERATING,2=FAULT`) |
| 1 | Shunt DTC LSB | `uint8` | `param::dtc` bits 7:0 |
| 2 | Shunt DTC MSB | `uint8` | `param::dtc` bits 15:8 |
| 3 | HV monitor state | enum (`uint8`) | `HVMonitorState` (`0=INIT,1=OPERATING,2=FAULT`) |
| 4 | HV monitor DTC | bitfield (`uint8`) | `HVMonitorDTC` |
| 5 | Voltage matched | bool (`uint8`) | `0=false`, `1=true` |
| 6-7 | HV monitor delta voltage | `uint16` | `V x 100` |

### `BMS_MSG_SHUNT_HV_I_CURRENT_COUNTERS` (0x603)

| Byte | Signal | Type | Scaling/Offset |
|-----|--------|------|----------------|
| 0-1 | Current | `uint16` | `(A x 10) + 5000` |
| 2-3 | Current average | `uint16` | `(A x 10) + 5000` |
| 4-5 | Charge counter (`As`) | `uint16` | `As + 32768` |
| 6-7 | Energy counter (`Wh`) | `uint16` | `Wh + 32768` |

### `BMS_MSG_SHUNT_HV_DI_TEMP_POWER` (0x604)

| Byte | Signal | Type | Scaling/Offset |
|-----|--------|------|----------------|
| 0-1 | Current derivative | `uint16` | `(A/s x 1) + 32768` |
| 2-3 | Temperature | `uint16` | `(degC x 10) + 400` |
| 4-5 | Power | `uint16` | `(W x 0.1) + 32768` |
| 6-7 | reserved | `uint16` | `0` |

### `BMS_MSG_SHUNT_HV_U12_U3` (0x605)

| Byte | Signal | Type | Scaling/Offset |
|-----|--------|------|----------------|
| 0-1 | Input HV box voltage (`U1`) | `uint16` | `V x 100` |
| 2-3 | Output HV box voltage (`U2`) | `uint16` | `V x 100` |
| 4-5 | `U3` voltage | `uint16` | `V x 100` |
| 6-7 | reserved | `uint16` | `0` |

## VCU to BMS (`BMS_VCU`, 0x437)

All signals below are unsigned unless otherwise noted.
Expand Down
10 changes: 10 additions & 0 deletions documentation/Shunt_HV_Monitor_Telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,13 @@ In the HV Monitor section, prints:

### Current visibility gap
- `param::hv_monitor_dtc` exists internally but is not currently printed by the serial monitor.

### CAN telemetry availability

All values listed in this document are now exported on BMS CAN via dedicated telemetry
messages (`0x602`-`0x605`) alongside contactor telemetry (`0x601`).

- `0x602` publishes shunt/HV monitor states and DTC bitfields.
- `0x603`..`0x605` publish scaled/offset `uint16` telemetry values.

See `documentation/CAN_Message_Packing.md` for exact byte-level layout.
65 changes: 65 additions & 0 deletions src/bms/battery_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "bms/battery_manager.h"
#include "bms/current.h"
#include "bms/contactor_manager.h"
#include "bms/hv_monitor.h"
#include "utils/can_packer.h"
#include "utils/can_crc.h"
#include "utils/current_limit_lookup.h"
Expand All @@ -15,6 +16,29 @@

// #define DEBUG


namespace {

uint16_t encodeUnsigned(float value, float scale, float offset)
{
const float encoded = (value * scale) + offset;
const float clamped = std::clamp(encoded, 0.0f, 65535.0f);
return static_cast<uint16_t>(clamped);
}

uint16_t encodeSignedOffset(float value, float scale)
{
return encodeUnsigned(value, scale, 32768.0f);
}

void writeUint16ToCanBytes(CANMessage &msg, uint8_t start_index, uint16_t value)
{
msg.data[start_index + 0] = static_cast<uint8_t>(value & 0xFF);
msg.data[start_index + 1] = static_cast<uint8_t>(value >> 8);
}

}

BMS::BMS(BatteryPack &_batteryPack, Shunt_IVTS &_shunt, Contactormanager &_contactorManager)
: batteryPack(_batteryPack),
shunt(_shunt),
Expand Down Expand Up @@ -135,6 +159,7 @@ void BMS::Task1000Ms()
void BMS::Monitor100Ms()
{
send_contactor_telemetry_message();
send_shunt_hv_monitor_telemetry_messages();
}

// ###############################################################################################################################################################################
Expand Down Expand Up @@ -652,6 +677,46 @@ void BMS::send_contactor_telemetry_message()
send_message(&msg);
}


void BMS::send_shunt_hv_monitor_telemetry_messages()
{
CANMessage msg;
msg.len = 8;

msg.id = BMS_MSG_SHUNT_HV_META;
msg.data[0] = static_cast<uint8_t>(param::state);
msg.data[1] = static_cast<uint8_t>(param::dtc & 0xFF);
msg.data[2] = static_cast<uint8_t>((param::dtc >> 8) & 0xFF);
msg.data[3] = static_cast<uint8_t>(param::hv_monitor_state);
msg.data[4] = static_cast<uint8_t>(param::hv_monitor_dtc);
msg.data[5] = param::voltage_matched ? 1U : 0U;
writeUint16ToCanBytes(msg, 6, encodeUnsigned(param::hv_monitor_delta_voltage, 100.0f, 0.0f));
send_message(&msg);

msg.id = BMS_MSG_SHUNT_HV_I_CURRENT_COUNTERS;
writeUint16ToCanBytes(msg, 0, encodeUnsigned(param::current, 10.0f, 5000.0f));
writeUint16ToCanBytes(msg, 2, encodeUnsigned(param::current_avg, 10.0f, 5000.0f));
writeUint16ToCanBytes(msg, 4, encodeSignedOffset(param::as, 1.0f));
writeUint16ToCanBytes(msg, 6, encodeSignedOffset(param::wh, 1.0f));
send_message(&msg);

msg.id = BMS_MSG_SHUNT_HV_DI_TEMP_POWER;
writeUint16ToCanBytes(msg, 0, encodeSignedOffset(param::current_dA_per_s, 1.0f));
writeUint16ToCanBytes(msg, 2, encodeUnsigned(param::temp, 10.0f, 400.0f));
writeUint16ToCanBytes(msg, 4, encodeSignedOffset(param::power, 0.1f));
msg.data[6] = 0U;
msg.data[7] = 0U;
send_message(&msg);

msg.id = BMS_MSG_SHUNT_HV_U12_U3;
writeUint16ToCanBytes(msg, 0, encodeUnsigned(param::u_input_hvbox, 100.0f, 0.0f));
writeUint16ToCanBytes(msg, 2, encodeUnsigned(param::u_output_hvbox, 100.0f, 0.0f));
writeUint16ToCanBytes(msg, 4, encodeUnsigned(param::u3, 100.0f, 0.0f));
msg.data[6] = 0U;
msg.data[7] = 0U;
send_message(&msg);
}

void BMS::send_message(CANMessage *frame)
{
if (ACAN_T4::BMS_CAN.tryToSend(*frame))
Expand Down
1 change: 1 addition & 0 deletions src/bms/battery_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class BMS

void send_battery_status_message();
void send_contactor_telemetry_message();
void send_shunt_hv_monitor_telemetry_messages();

// --- Core Functions ---
void update_soc_coulomb_counting();
Expand Down
4 changes: 4 additions & 0 deletions src/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@
#define BMS_MSG_SOC 0x41D
#define BMS_MSG_HMI 0x41E
#define BMS_MSG_CONTACTOR_TELEMETRY 0x601
#define BMS_MSG_SHUNT_HV_META 0x602
#define BMS_MSG_SHUNT_HV_I_CURRENT_COUNTERS 0x603
#define BMS_MSG_SHUNT_HV_DI_TEMP_POWER 0x604
#define BMS_MSG_SHUNT_HV_U12_U3 0x605
#define BMS_VCU_TIMEOUT 300

#define BMS_ENERGY_AVG_WINDOW_SEC 60.0f
Expand Down