#pragma once #include "opendbc/safety/safety_declarations.h" static uint8_t rivian_get_counter(const CANPacket_t *msg) { uint8_t cnt = 0; if ((msg->addr == 0x208U) || (msg->addr == 0x150U)) { // Signal: ESP_Status_Counter, VDM_PropStatus_Counter cnt = msg->data[1] & 0xFU; } return cnt; } static uint32_t rivian_get_checksum(const CANPacket_t *msg) { uint8_t chksum = 0; if ((msg->addr == 0x208U) || (msg->addr == 0x150U)) { // Signal: ESP_Status_Checksum, VDM_PropStatus_Checksum chksum = msg->data[0]; } else { } return chksum; } static uint8_t _rivian_compute_checksum(const CANPacket_t *msg, uint8_t poly, uint8_t xor_output) { int len = GET_LEN(msg); uint8_t crc = 0; // Skip the checksum byte for (int i = 1; i < len; i++) { crc ^= msg->data[i]; for (int j = 0; j < 8; j++) { if ((crc & 0x80U) != 0U) { crc = (crc << 1) ^ poly; } else { crc <<= 1; } } } return crc ^ xor_output; } static uint32_t rivian_compute_checksum(const CANPacket_t *msg) { uint8_t chksum = 0; if (msg->addr == 0x208U) { chksum = _rivian_compute_checksum(msg, 0x1D, 0xB1); } else if (msg->addr == 0x150U) { chksum = _rivian_compute_checksum(msg, 0x1D, 0x9A); } else { } return chksum; } static bool rivian_get_quality_flag_valid(const CANPacket_t *msg) { bool valid = false; if (msg->addr == 0x208U) { valid = ((msg->data[3] >> 3) & 0x3U) == 0x1U; // ESP_Vehicle_Speed_Q } else if (msg->addr == 0x150U) { valid = (msg->data[1] >> 6) == 0x1U; // VDM_VehicleSpeedQ } else { } return valid; } static void rivian_rx_hook(const CANPacket_t *msg) { if (msg->bus == 0U) { // Vehicle speed if (msg->addr == 0x208U) { float speed = ((msg->data[6] << 8) | msg->data[7]) * 0.01; vehicle_moving = speed > 0.0; UPDATE_VEHICLE_SPEED(speed * KPH_TO_MS); } // Gas pressed and second speed source for variable torque limit if (msg->addr == 0x150U) { gas_pressed = msg->data[3] | (msg->data[4] & 0xC0U); // Disable controls if speeds from VDM and ESP ECUs are too far apart. float vdm_speed = ((msg->data[5] << 8) | msg->data[6]) * 0.01 * KPH_TO_MS; speed_mismatch_check(vdm_speed); } // Driver torque if (msg->addr == 0x380U) { int torque_driver_new = (((msg->data[2] << 4) | (msg->data[3] >> 4))) - 2050U; update_sample(&torque_driver, torque_driver_new); } // Brake pressed if (msg->addr == 0x38fU) { brake_pressed = (msg->data[2] >> 7) & 1U; } } if (msg->bus == 2U) { // Cruise state if (msg->addr == 0x100U) { const int feature_status = msg->data[2] >> 5U; pcm_cruise_check(feature_status == 1); } } } static bool rivian_tx_hook(const CANPacket_t *msg) { // Rivian utilizes more torque at low speed to maintain the same lateral accel const TorqueSteeringLimits RIVIAN_STEERING_LIMITS = { .max_torque = 350, .dynamic_max_torque = true, .max_torque_lookup = { {9., 17., 17.}, {350, 250, 250}, }, .max_rate_up = 3, .max_rate_down = 5, .max_rt_delta = 125, .driver_torque_multiplier = 2, .driver_torque_allowance = 100, .type = TorqueDriverLimited, }; const LongitudinalLimits RIVIAN_LONG_LIMITS = { .max_accel = 200, .min_accel = -350, .inactive_accel = 0, }; bool tx = true; if (msg->bus == 0U) { // Steering control if (msg->addr == 0x120U) { int desired_torque = ((msg->data[2] << 3U) | (msg->data[3] >> 5U)) - 1024U; bool steer_req = (msg->data[3] >> 4) & 1U; if (steer_torque_cmd_checks(desired_torque, steer_req, RIVIAN_STEERING_LIMITS)) { tx = false; } } // Longitudinal control if (msg->addr == 0x160U) { int raw_accel = ((msg->data[2] << 3) | (msg->data[3] >> 5)) - 1024U; if (longitudinal_accel_checks(raw_accel, RIVIAN_LONG_LIMITS)) { tx = false; } } } return tx; } static safety_config rivian_init(uint16_t param) { // SCCM_WheelTouch: for hiding hold wheel alert // VDM_AdasSts: for canceling stock ACC // 0x120 = ACM_lkaHbaCmd, 0x321 = SCCM_WheelTouch, 0x162 = VDM_AdasSts static const CanMsg RIVIAN_TX_MSGS[] = {{0x120, 0, 8, .check_relay = true}, {0x321, 2, 7, .check_relay = true}, {0x162, 2, 8, .check_relay = true}}; // 0x160 = ACM_longitudinalRequest static const CanMsg RIVIAN_LONG_TX_MSGS[] = {{0x120, 0, 8, .check_relay = true}, {0x321, 2, 7, .check_relay = true}, {0x160, 0, 5, .check_relay = true}}; static RxCheck rivian_rx_checks[] = { {.msg = {{0x208, 0, 8, 50U, .max_counter = 14U}, { 0 }, { 0 }}}, // ESP_Status (speed) {.msg = {{0x150, 0, 7, 50U, .max_counter = 14U}, { 0 }, { 0 }}}, // VDM_PropStatus (gas pedal & 2nd speed) {.msg = {{0x380, 0, 5, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // EPAS_SystemStatus (driver torque) {.msg = {{0x38f, 0, 6, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // iBESP2 (brakes) {.msg = {{0x100, 2, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ACM_Status (cruise state) }; bool rivian_longitudinal = false; UNUSED(param); #ifdef ALLOW_DEBUG const int FLAG_RIVIAN_LONG_CONTROL = 1; rivian_longitudinal = GET_FLAG(param, FLAG_RIVIAN_LONG_CONTROL); #endif // FIXME: cppcheck thinks that rivian_longitudinal is always false. This is not true // if ALLOW_DEBUG is defined but cppcheck is run without ALLOW_DEBUG // cppcheck-suppress knownConditionTrueFalse return rivian_longitudinal ? BUILD_SAFETY_CFG(rivian_rx_checks, RIVIAN_LONG_TX_MSGS) : \ BUILD_SAFETY_CFG(rivian_rx_checks, RIVIAN_TX_MSGS); } const safety_hooks rivian_hooks = { .init = rivian_init, .rx = rivian_rx_hook, .tx = rivian_tx_hook, .get_counter = rivian_get_counter, .get_checksum = rivian_get_checksum, .compute_checksum = rivian_compute_checksum, .get_quality_flag_valid = rivian_get_quality_flag_valid, };