Tesla: MADS full support with VEHICLE bus harness (#239)

* Tesla: MADS full support with VEHICLE bus harness

* fix msg size

* enable safety

* safety tests

* 6 instead of 8 bytes

* Revert "6 instead of 8 bytes"

This reverts commit 240db92d37397b67196a5dbf52fcf4f7d63d3104.
This commit is contained in:
Jason Wen
2025-10-02 14:07:08 -04:00
committed by GitHub
parent b0985d3857
commit 296f6b920c
8 changed files with 176 additions and 16 deletions

View File

@@ -5,12 +5,15 @@ from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.interfaces import CarStateBase
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, CAR
from opendbc.sunnypilot.car.tesla.carstate_ext import CarStateExt
ButtonType = structs.CarState.ButtonEvent.Type
class CarState(CarStateBase):
class CarState(CarStateBase, CarStateExt):
def __init__(self, CP, CP_SP):
super().__init__(CP, CP_SP)
CarStateBase.__init__(self, CP, CP_SP)
CarStateExt.__init__(self, CP, CP_SP)
self.can_define = CANDefine(DBC[CP.carFingerprint][Bus.party])
self.shifter_values = self.can_define.dv["DI_systemStatus"]["DI_gear"]
@@ -118,11 +121,14 @@ class CarState(CarStateBase):
# Messages needed by carcontroller
self.das_control = copy.copy(cp_ap_party.vl["DAS_control"])
CarStateExt.update(self, ret, can_parsers)
return ret, ret_sp
@staticmethod
def get_can_parsers(CP, CP_SP):
return {
Bus.party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.party),
Bus.ap_party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.autopilot_party)
Bus.ap_party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.autopilot_party),
**CarStateExt.get_parser(CP, CP_SP),
}

View File

@@ -4,6 +4,8 @@ from opendbc.car.tesla.carcontroller import CarController
from opendbc.car.tesla.carstate import CarState
from opendbc.car.tesla.values import TeslaSafetyFlags, CAR
from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP, TeslaSafetyFlagsSP
class CarInterface(CarInterfaceBase):
CarState = CarState
@@ -41,4 +43,8 @@ class CarInterface(CarInterfaceBase):
stock_cp.enableBsm = True
if 0x3DF in fingerprint[1]:
ret.flags |= TeslaFlagsSP.HAS_VEHICLE_BUS.value
ret.safetyParam |= TeslaSafetyFlagsSP.HAS_VEHICLE_BUS
return ret

View File

@@ -37,7 +37,7 @@ class TeslaCarDocsHW4(CarDocs):
@dataclass
class TeslaPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.party: 'tesla_model3_party'})
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.party: 'tesla_model3_party', Bus.adas: 'tesla_model3_vehicle'})
class CAR(Platforms):

View File

@@ -236,6 +236,48 @@ BO_ 1013 ID3F5VCFRONT_lighting: 8 VEH
SG_ VCFRONT_indicatorRightRequest : 2|2@1+ (1,0) [0|2] "" Receiver
SG_ VCFRONT_indicatorLeftRequest : 0|2@1+ (1,0) [0|2] "" Receiver
BO_ 851 UI_status: 8 VehicleBus
SG_ UI_touchActive : 0|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_audioActive : 1|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_bluetoothActive : 2|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_cellActive : 3|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_displayReady : 4|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_displayOn : 5|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_wifiActive : 6|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_wifiConnected : 7|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_systemActive : 8|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_readyForDrive : 9|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_cellConnected : 10|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_vpnActive : 11|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_autopilotTrial : 12|2@1+ (1,0) [0|3] "" VehicleBus
SG_ UI_factoryReset : 14|2@1+ (1,0) [0|3] "" VehicleBus
SG_ UI_gpsActive : 16|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_screenshotActive : 17|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_radioActive : 18|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_cellNetworkTechnology : 19|4@1+ (1,0) [0|15] "" VehicleBus
SG_ UI_cellReceiverPower : 24|8@1+ (1,-128) [-128|127] "dB" VehicleBus
SG_ UI_falseTouchCounter : 32|8@1+ (1,0) [0|255] "1" VehicleBus
SG_ UI_developmentCar : 40|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_cameraActive : 41|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_cellSignalBars : 42|3@1+ (1,0) [0|7] "" VehicleBus
SG_ UI_trailerModeTelltale : 45|2@1+ (1,0) [0|3] "" VehicleBus
SG_ UI_pcbTemperature : 48|8@1- (1,40) [-87|167] "degC" VehicleBus
SG_ UI_cpuTemperature : 56|8@1- (1,40) [-87|167] "degC" VehicleBus
BO_ 991 UI_status2: 8 VehicleBus
SG_ UI_mobileAppStepCount : 0|16@1+ (1,0) [0|65535] "" VehicleBus
SG_ UI_userRequestSnapshot : 16|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_touchDetected : 17|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_validDeviceForEUSummon : 18|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_locatedAtHome : 19|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_locatedAtWork : 20|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_locatedAtFavorite : 21|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_displayInDarkMode : 22|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_userActivity : 23|1@1+ (1,0) [0|1] "" VehicleBus
SG_ UI_activeTouchPoints : 24|8@1+ (1,0) [0|255] "" VehicleBus
SG_ UI_linkState : 32|2@1+ (1,0) [0|3] "" VehicleBus
SG_ UI_sentryModeState : 34|3@1+ (1,0) [0|7] "" VehicleBus
VAL_ 568 SpdCtrlLvr_Stat 32 "DN_1ST" 16 "UP_1ST" 8 "DN_2ND" 4 "UP_2ND" 2 "RWD" 1 "FWD" 0 "IDLE" ;
VAL_ 568 DTR_Dist_Rq 255 "SNA" 200 "ACC_DIST_7" 166 "ACC_DIST_6" 133 "ACC_DIST_5" 100 "ACC_DIST_4" 66 "ACC_DIST_3" 33 "ACC_DIST_2" 0 "ACC_DIST_1" ;
VAL_ 568 TurnIndLvr_Stat 3 "SNA" 2 "RIGHT" 1 "LEFT" 0 "IDLE" ;

View File

@@ -2,6 +2,20 @@
#include "opendbc/safety/safety_declarations.h"
#define TESLA_COMMON_RX_CHECKS \
{.msg = {{0x2b9, 2, 8, 25U, .max_counter = 7U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* DAS_control */ \
{.msg = {{0x488, 2, 4, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* DAS_steeringControl */ \
{.msg = {{0x257, 0, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* DI_speed (speed in kph) */ \
{.msg = {{0x155, 0, 8, 50U, .max_counter = 15U}, { 0 }, { 0 }}}, /* ESP_B (2nd speed in kph) */ \
{.msg = {{0x370, 0, 8, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* EPAS3S_sysStatus (steering angle) */ \
{.msg = {{0x118, 0, 8, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* DI_systemStatus (gas pedal) */ \
{.msg = {{0x39d, 0, 5, 25U, .max_counter = 15U}, { 0 }, { 0 }}}, /* IBST_status (brakes) */ \
{.msg = {{0x286, 0, 8, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* DI_state (acc state) */ \
{.msg = {{0x311, 0, 7, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* UI_warning (blinkers, buckle switch & doors) */ \
#define TESLA_VEHICLE_BUS_ADDR_CHECK \
{.msg = {{0x3DF, 1, 8, 2U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, /* UI_status2 */ \
static bool tesla_longitudinal = false;
static bool tesla_stock_aeb = false;
@@ -14,6 +28,10 @@ static bool tesla_stock_lkas_prev = false;
static bool tesla_autopark = false;
static bool tesla_autopark_prev = false;
// Detected VEHICLE bus
extern bool tesla_has_vehicle_bus;
bool tesla_has_vehicle_bus = false;
static uint8_t tesla_get_counter(const CANPacket_t *msg) {
uint8_t cnt = 0;
@@ -166,6 +184,12 @@ static void tesla_rx_hook(const CANPacket_t *msg) {
}
}
if (msg->bus == 1U) {
if (msg->addr == 0x3DFU) {
mads_button_press = (msg->data[3] == 3U) ? MADS_BUTTON_PRESSED : MADS_BUTTON_NOT_PRESSED;
}
}
if (msg->bus == 2U) {
// DAS_control
if (msg->addr == 0x2b9U) {
@@ -336,6 +360,10 @@ static safety_config tesla_init(uint16_t param) {
tesla_longitudinal = GET_FLAG(param, TESLA_FLAG_LONGITUDINAL_CONTROL);
#endif
const int TESLA_PARAM_SP_VEHICLE_BUS = 1;
tesla_has_vehicle_bus = GET_FLAG(current_safety_param_sp, TESLA_PARAM_SP_VEHICLE_BUS);
tesla_stock_aeb = false;
tesla_stock_lkas = false;
tesla_stock_lkas_prev = false;
@@ -345,22 +373,25 @@ static safety_config tesla_init(uint16_t param) {
tesla_autopark_prev = false;
static RxCheck tesla_model3_y_rx_checks[] = {
{.msg = {{0x2b9, 2, 8, 25U, .max_counter = 7U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // DAS_control
{.msg = {{0x488, 2, 4, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // DAS_steeringControl
{.msg = {{0x257, 0, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // DI_speed (speed in kph)
{.msg = {{0x155, 0, 8, 50U, .max_counter = 15U}, { 0 }, { 0 }}}, // ESP_B (2nd speed in kph)
{.msg = {{0x370, 0, 8, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // EPAS3S_sysStatus (steering angle)
{.msg = {{0x118, 0, 8, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // DI_systemStatus (gas pedal)
{.msg = {{0x39d, 0, 5, 25U, .max_counter = 15U}, { 0 }, { 0 }}}, // IBST_status (brakes)
{.msg = {{0x286, 0, 8, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // DI_state (acc state)
{.msg = {{0x311, 0, 7, 10U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // UI_warning (blinkers, buckle switch & doors)
TESLA_COMMON_RX_CHECKS
};
static RxCheck tesla_model3_y_vehicle_bus_rx_checks[] = {
TESLA_COMMON_RX_CHECKS
TESLA_VEHICLE_BUS_ADDR_CHECK
};
safety_config ret;
if (tesla_longitudinal) {
ret = BUILD_SAFETY_CFG(tesla_model3_y_rx_checks, TESLA_M3_Y_LONG_TX_MSGS);
SET_TX_MSGS(TESLA_M3_Y_LONG_TX_MSGS, ret);
} else {
ret = BUILD_SAFETY_CFG(tesla_model3_y_rx_checks, TESLA_M3_Y_TX_MSGS);
SET_TX_MSGS(TESLA_M3_Y_TX_MSGS, ret);
}
if (tesla_has_vehicle_bus) {
SET_RX_CHECKS(tesla_model3_y_vehicle_bus_rx_checks, ret);
} else {
SET_RX_CHECKS(tesla_model3_y_rx_checks, ret);
}
return ret;
}

View File

@@ -4,7 +4,7 @@ import unittest
import numpy as np
from opendbc.car.lateral import get_max_angle_delta_vm, get_max_angle_vm
from opendbc.car.tesla.values import CarControllerParams, TeslaSafetyFlags
from opendbc.car.tesla.values import CarControllerParams, TeslaSafetyFlags, CANBUS
from opendbc.car.tesla.carcontroller import get_safety_CP
from opendbc.car.structs import CarParams
from opendbc.car.vehicle_model import VehicleModel
@@ -13,6 +13,8 @@ from opendbc.safety.tests.libsafety import libsafety_py
import opendbc.safety.tests.common as common
from opendbc.safety.tests.common import CANPackerPanda, MAX_SPEED_DELTA, MAX_WRONG_COUNTERS, away_round, round_speed
from opendbc.sunnypilot.car.tesla.values import TeslaSafetyFlagsSP
MSG_DAS_steeringControl = 0x488
MSG_APS_eacMonitor = 0x27d
MSG_DAS_Control = 0x2b9
@@ -448,5 +450,22 @@ class TestTeslaLongitudinalSafety(TestTeslaSafetyBase):
self.assertFalse(self._tx(self._long_control_msg(set_speed=0, accel_limits=(-0.1, -0.1))))
class TestTeslaVehicleBusSafety(TestTeslaSafetyBase):
LONGITUDINAL = False
def setUp(self):
super().setUp()
self.safety = libsafety_py.libsafety
self.packer_adas = CANPackerPanda("tesla_model3_vehicle")
self.safety.set_current_safety_param_sp(TeslaSafetyFlagsSP.HAS_VEHICLE_BUS)
self.safety.set_safety_hooks(CarParams.SafetyModel.tesla, 0)
self.safety.init_tests()
def _lkas_button_msg(self, enabled):
values = {"UI_activeTouchPoints": 3 if enabled else 0}
return self.packer_adas.make_can_msg_panda("UI_status2", CANBUS.vehicle, values)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,41 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from enum import StrEnum
from opendbc.car import Bus, create_button_events, structs
from opendbc.can.parser import CANParser
from opendbc.car.tesla.values import DBC, CANBUS
from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP
ButtonType = structs.CarState.ButtonEvent.Type
class CarStateExt:
def __init__(self, CP: structs.CarParams, CP_SP: structs.CarParamsSP):
self.CP = CP
self.CP_SP = CP_SP
self.infotainment_3_finger_press = 0
def update(self, ret: structs.CarState, can_parsers: dict[StrEnum, CANParser]) -> None:
if self.CP_SP.flags & TeslaFlagsSP.HAS_VEHICLE_BUS:
cp_adas = can_parsers[Bus.adas]
prev_infotainment_3_finger_press = self.infotainment_3_finger_press
self.infotainment_3_finger_press = int(cp_adas.vl["UI_status2"]["UI_activeTouchPoints"])
ret.buttonEvents = [*create_button_events(self.infotainment_3_finger_press, prev_infotainment_3_finger_press,
{3: ButtonType.lkas})]
@staticmethod
def get_parser(CP: structs.CarParams, CP_SP: structs.CarParamsSP) -> dict[StrEnum, CANParser]:
messages = {}
if CP_SP.flags & TeslaFlagsSP.HAS_VEHICLE_BUS:
messages[Bus.adas] = CANParser(DBC[CP.carFingerprint][Bus.adas], [], CANBUS.vehicle)
return messages

View File

@@ -0,0 +1,15 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from enum import IntFlag
class TeslaFlagsSP(IntFlag):
HAS_VEHICLE_BUS = 1 # 3-finger infotainment press signal is present on the VEHICLE bus with the deprecated Tesla harness installed
class TeslaSafetyFlagsSP:
HAS_VEHICLE_BUS = 1