byd: fix SEAL bus topology from live dump — ADAS on bus 0, PCM_BUTTONS confirmed

Live bus dump from the Seal showed a different topology than the Han:
- ACC_HUD_ADAS (813) + ACC_CMD (814) are broadcast on the vehicle CAN
  (bus 0), not the camera CAN. Panda rx check expected 813 on bus 2 and
  never saw it -> safety config invalid -> controls blocked forever.
- PCM_BUTTONS (944) confirmed on bus 0 at 20Hz (MADS button placement OK)
- 482 MPC_LKAS_CMD_ANGLE present on bus 2 (angle LKAS cmd; for future use)

Changes:
- carstate: ADAS messages (ACC_HUD_ADAS/ACC_CMD) read from bus 0 on SEAL,
  bus 2 on HAN (cp_adas accessor); parsers built per platform/bus
- panda byd.h: 813 cruise check is bus-independent; rx checks accept
  813 on bus 0 or 2, 944 on bus 0 or 2 (first-seen-wins)
- interface: disable openpilot longitudinal for SEAL — stock ACC_CMD
  originates on bus 0 where the panda cannot intercept it. Stock ACC +
  MADS lateral it is (which is the point of MADS anyway).
- panda firmware rebuilt

Verified: SEAL (cruise+lkas button via bus 0, alphaLong=False) and
HAN (bus 2 topology, alphaLong=True) both canValid; 120 safety tests +
MISRA clean.
This commit is contained in:
Quintine committed 2026-07-25 18:57:17 +10:00
1 parent 5684dd9d0e
commit 1abd13736b
21 files changed
+46 -30

No files matched your search

+28 -15
View File
@@ -22,6 +22,10 @@ class CarState(CarStateBase, MadsCarState):
else:
self.steer_msg = "STEERING_TORQUE"
# ADAS cruise state (ACC_HUD_ADAS) and stock ACC echo (ACC_CMD) are on
# the vehicle CAN (bus 0) on the SEAL, camera CAN (bus 2) on the HAN
self.adas_on_pt_bus = CP.carFingerprint == CAR.BYD_SEAL_PERFORMANCE_25
self.mpc_lkas_cmd_msg = None
self.eps_steering_torque_msg = None
self.eps_prepared = False
@@ -35,6 +39,7 @@ class CarState(CarStateBase, MadsCarState):
def update(self, can_parsers) -> tuple[structs.CarState, structs.CarStateSP]:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
cp_adas = cp if self.adas_on_pt_bus else cp_cam
ret = structs.CarState()
ret_sp = structs.CarStateSP()
@@ -59,13 +64,13 @@ class CarState(CarStateBase, MadsCarState):
ret.steerFaultPermanent = cp.vl[self.steer_msg]["TORQUE_FAILED"] != 0
ret.steerFaultTemporary = cp.vl[self.steer_msg]["TORQUE_FAILED"] != 0
ret.stockAeb = cp_cam.vl["ACC_HUD_ADAS"]["AEB"] == 1
ret.stockFcw = cp_cam.vl["ACC_HUD_ADAS"]["FCW"] == 1 # Forward Collision Warning
ret.stockAeb = cp_adas.vl["ACC_HUD_ADAS"]["AEB"] == 1
ret.stockFcw = cp_adas.vl["ACC_HUD_ADAS"]["FCW"] == 1 # Forward Collision Warning
# Cruise state
ret.cruiseState.enabled = cp_cam.vl["ACC_HUD_ADAS"]["CRUISE_STATE"] in (3, 5) # (Active, Override)
ret.cruiseState.speed = cp_cam.vl["ACC_HUD_ADAS"]["SET_SPEED"] * 10 * CV.KPH_TO_MS
ret.cruiseState.available = cp_cam.vl["ACC_HUD_ADAS"]["CRUISE_STATE"] not in (8, 9) # (Failure, PermanentFailure)
ret.cruiseState.enabled = cp_adas.vl["ACC_HUD_ADAS"]["CRUISE_STATE"] in (3, 5) # (Active, Override)
ret.cruiseState.speed = cp_adas.vl["ACC_HUD_ADAS"]["SET_SPEED"] * 10 * CV.KPH_TO_MS
ret.cruiseState.available = cp_adas.vl["ACC_HUD_ADAS"]["CRUISE_STATE"] not in (8, 9) # (Failure, PermanentFailure)
ret.cruiseState.standstill = False # This needs to be false, since we can resume from stop without sending anything special
# Gear
@@ -102,7 +107,7 @@ class CarState(CarStateBase, MadsCarState):
self.mpc_lkas_active = cp_cam.vl["MPC_LKAS_CMD"]["LKAS_ACTIVE"] != 0
self.mpc_lkas_request_prepare = cp_cam.vl["MPC_LKAS_CMD"]["LKASPrepare"] != 0
# for generate ACC_CMD
self.acc_cmd_msg = copy.copy(cp_cam.vl["ACC_CMD"])
self.acc_cmd_msg = copy.copy(cp_adas.vl["ACC_CMD"])
return ret, ret_sp
@staticmethod
@@ -123,6 +128,23 @@ class CarState(CarStateBase, MadsCarState):
("PCM_BUTTONS", float('nan')),
]
cam_messages = [
# stock camera command echoes, only present when stock ADAS is active
("MPC_LKAS_CMD", float('nan')),
# SEAL camera also broadcasts an angle-based LKAS command (parsed for future use)
("MPC_LKAS_CMD_ANGLE", float('nan')),
# PCM_BUTTONS may be forwarded on the camera bus on some harnesses
("PCM_BUTTONS", float('nan')),
]
# ADAS cruise state and stock ACC echo: vehicle CAN (bus 0) on the SEAL,
# camera CAN (bus 2) on the HAN. Alive-check ACC_HUD_ADAS only on the bus it lives on.
adas_messages = [("ACC_HUD_ADAS", 10), ("ACC_CMD", float('nan'))]
if CP.carFingerprint == CAR.BYD_SEAL_PERFORMANCE_25:
pt_messages.extend(adas_messages)
else:
cam_messages.extend(adas_messages)
# EPS steering feedback message differs by platform; alive-check only
# the one this car has, parse the other opportunistically
if CP.carFingerprint == CAR.BYD_SEAL_PERFORMANCE_25:
@@ -132,15 +154,6 @@ class CarState(CarStateBase, MadsCarState):
pt_messages.append(("STEERING_TORQUE", 10))
pt_messages.append(("STEERING_TORQUE_ANGLE", float('nan')))
cam_messages = [
("ACC_HUD_ADAS", 10),
# stock camera command echoes, only present when stock ADAS is active
("MPC_LKAS_CMD", float('nan')),
("ACC_CMD", float('nan')),
# PCM_BUTTONS may be forwarded on the camera bus on some harnesses
("PCM_BUTTONS", float('nan')),
]
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, CANBUS.main_bus),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, CANBUS.cam_bus),
+5 -2
View File
@@ -32,8 +32,11 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# longitudinal control parameters
ret.alphaLongitudinalAvailable = True
if alpha_long:
# HAN: openpilot long available (ACC_CMD replaced on the tapped camera bus).
# SEAL: stock ACC_CMD is broadcast directly on the vehicle CAN (bus 0),
# so it cannot be intercepted/replaced by the panda — stock ACC only (MADS).
ret.alphaLongitudinalAvailable = candidate != CAR.BYD_SEAL_PERFORMANCE_25
if alpha_long and ret.alphaLongitudinalAvailable:
ret.openpilotLongitudinalControl = True
ret.startingState = True
ret.startAccel = 0.1
+11 -11
View File
@@ -65,6 +65,13 @@ static bool byd_tx_hook(const CANPacket_t *msg) {
}
static void byd_rx_hook(const CANPacket_t *msg) {
// ACC_HUD_ADAS: cruise state. Vehicle CAN (bus 0) on SEAL, camera CAN (bus 2) on HAN
if (msg->addr == 813U) {
// CRUISE_STATE in (3, 5) == (Active, Override)
uint8_t cruise_state = (msg->data[5] >> 4U) & 0x0FU;
pcm_cruise_check((cruise_state == 3U) || (cruise_state == 5U));
}
// Main Bus
if (msg->bus == 0U) {
// PEDAL
@@ -101,15 +108,6 @@ static void byd_rx_hook(const CANPacket_t *msg) {
if (msg->addr == 944U) {
mads_button_press = GET_BIT(msg, BYD_LKAS_ON_BTN_BIT) ? MADS_BUTTON_PRESSED : MADS_BUTTON_NOT_PRESSED;
}
}
// Cam Bus
else if (msg->bus == 2U) {
// ACC_HUD_ADAS
if (msg->addr == 813U) {
// CRUISE_STATE in (3, 5) == (Active, Override)
uint8_t cruise_state = (msg->data[5] >> 4U) & 0x0FU;
pcm_cruise_check((cruise_state == 3U) || (cruise_state == 5U));
}
} else {
}
}
@@ -134,8 +132,10 @@ static safety_config byd_init(uint16_t param) {
{.msg = {{287, 0, 5, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STEER_MODULE
{.msg = {{792, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, // STEERING_TORQUE (HAN)
{508, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }}}, // STEERING_TORQUE_ANGLE (SEAL)
{.msg = {{944, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // PCM_BUTTONS
{.msg = {{813, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ACC_HUD_ADAS
{.msg = {{944, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, // PCM_BUTTONS (vehicle CAN)
{944, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }}}, // PCM_BUTTONS (camera CAN)
{.msg = {{813, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, // ACC_HUD_ADAS (SEAL: vehicle CAN)
{813, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }}}, // ACC_HUD_ADAS (HAN: camera CAN)
{.msg = {{307, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STALKS
{.msg = {{660, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // METER_CLUSTER
{.msg = {{790, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // MPC_LKAS_CMD