From fec966d6fd099dc140997978765733734fe6d1c3 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 17 Nov 2021 17:27:24 -0800 Subject: [PATCH] Honda: handle ACC state consistent with ISO 15622:2018 (#789) --- board/safety/safety_honda.h | 50 +++++++++++++++++++++++++++------- board/safety_declarations.h | 2 ++ python/__init__.py | 4 +++ tests/safety/common.py | 3 ++- tests/safety/test_honda.py | 54 +++++++++++++++++++++++++++++++------ 5 files changed, 94 insertions(+), 19 deletions(-) diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h index 8b797e67..c305901d 100644 --- a/board/safety/safety_honda.h +++ b/board/safety/safety_honda.h @@ -25,13 +25,23 @@ const int HONDA_BOSCH_GAS_MAX = 2000; const int HONDA_BOSCH_ACCEL_MIN = -350; // max braking == -3.5m/s2 // Nidec and Bosch giraffe have pt on bus 0 -AddrCheckStruct honda_addr_checks[] = { +AddrCheckStruct honda_nidec_addr_checks[] = { {.msg = {{0x1A6, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, - {0x296, 0, 4, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U},{ 0 }}}, + {0x296, 0, 4, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, { 0 }}}, {.msg = {{0x158, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, {.msg = {{0x17C, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, }; -#define HONDA_ADDR_CHECKS_LEN (sizeof(honda_addr_checks) / sizeof(honda_addr_checks[0])) +#define HONDA_NIDEC_ADDR_CHECKS_LEN (sizeof(honda_nidec_addr_checks) / sizeof(honda_nidec_addr_checks[0])) + +// For Nidecs with main on signal on 0x326 +AddrCheckStruct honda_nidec_alt_addr_checks[] = { + {.msg = {{0x1A6, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, + {0x296, 0, 4, .check_checksum = true, .max_counter = 3U, .expected_timestep = 40000U}, { 0 }}}, + {.msg = {{0x158, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x17C, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x326, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 100000U}, { 0 }, { 0 }}}, +}; +#define HONDA_NIDEC_ALT_ADDR_CHECKS_LEN (sizeof(honda_nidec_alt_addr_checks) / sizeof(honda_nidec_alt_addr_checks[0])) // Bosch harness has pt on bus 1 AddrCheckStruct honda_bh_addr_checks[] = { @@ -39,18 +49,20 @@ AddrCheckStruct honda_bh_addr_checks[] = { {.msg = {{0x158, 1, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, {.msg = {{0x17C, 1, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, {0x1BE, 1, 3, .check_checksum = true, .max_counter = 3U, .expected_timestep = 20000U}, { 0 }}}, + {.msg = {{0x326, 1, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 100000U}, { 0 }, { 0 }}}, }; #define HONDA_BH_ADDR_CHECKS_LEN (sizeof(honda_bh_addr_checks) / sizeof(honda_bh_addr_checks[0])) const uint16_t HONDA_PARAM_ALT_BRAKE = 1; const uint16_t HONDA_PARAM_BOSCH_LONG = 2; +const uint16_t HONDA_PARAM_NIDEC_ALT = 4; int honda_brake = 0; bool honda_alt_brake_msg = false; bool honda_fwd_brake = false; bool honda_bosch_long = false; enum {HONDA_N_HW, HONDA_BG_HW, HONDA_BH_HW} honda_hw = HONDA_N_HW; -addr_checks honda_rx_checks = {honda_addr_checks, HONDA_ADDR_CHECKS_LEN}; +addr_checks honda_rx_checks = {honda_nidec_addr_checks, HONDA_NIDEC_ADDR_CHECKS_LEN}; static uint8_t honda_get_checksum(CANPacket_t *to_push) { @@ -96,17 +108,30 @@ static int honda_rx_hook(CANPacket_t *to_push) { vehicle_moving = GET_BYTE(to_push, 0) | GET_BYTE(to_push, 1); } + // check ACC main state + // 0x326 for all Bosch and some Nidec, 0x1A6 for some Nidec + if ((addr == 0x326) || (addr == 0x1A6)) { + acc_main_on = GET_BIT(to_push, (addr == 0x326) ? 28 : 47); + if (!acc_main_on) { + controls_allowed = 0; + } + } + // state machine to enter and exit controls // 0x1A6 for the ILX, 0x296 for the Civic Touring if ((addr == 0x1A6) || (addr == 0x296)) { + // check for button presses int button = (GET_BYTE(to_push, 0) & 0xE0) >> 5; switch (button) { + case 1: // main case 2: // cancel controls_allowed = 0; break; case 3: // set case 4: // resume - controls_allowed = 1; + if (acc_main_on) { + controls_allowed = 1; + } break; default: break; // any other button is irrelevant @@ -292,9 +317,9 @@ static int honda_tx_hook(CANPacket_t *to_send) { // Only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address if (addr == 0x18DAB0F1) { - if ((GET_BYTES_04(to_send) != 0x00803E02) || (GET_BYTES_48(to_send) != 0x0)) { - tx = 0; - } + if ((GET_BYTES_04(to_send) != 0x00803E02) || (GET_BYTES_48(to_send) != 0x0)) { + tx = 0; + } } // 1 allows the message through @@ -309,7 +334,12 @@ static const addr_checks* honda_nidec_init(int16_t param) { honda_hw = HONDA_N_HW; honda_alt_brake_msg = false; honda_bosch_long = false; - honda_rx_checks = (addr_checks){honda_addr_checks, HONDA_ADDR_CHECKS_LEN}; + + if (GET_FLAG(param, HONDA_PARAM_NIDEC_ALT)) { + honda_rx_checks = (addr_checks){honda_nidec_alt_addr_checks, HONDA_NIDEC_ALT_ADDR_CHECKS_LEN}; + } else { + honda_rx_checks = (addr_checks){honda_nidec_addr_checks, HONDA_NIDEC_ADDR_CHECKS_LEN}; + } return &honda_rx_checks; } @@ -325,7 +355,7 @@ static const addr_checks* honda_bosch_giraffe_init(int16_t param) { honda_bosch_long = GET_FLAG(param, HONDA_PARAM_BOSCH_LONG); #endif - honda_rx_checks = (addr_checks){honda_addr_checks, HONDA_ADDR_CHECKS_LEN}; + honda_rx_checks = (addr_checks){honda_nidec_addr_checks, HONDA_NIDEC_ADDR_CHECKS_LEN}; return &honda_rx_checks; } diff --git a/board/safety_declarations.h b/board/safety_declarations.h index 4754738f..d5705b48 100644 --- a/board/safety_declarations.h +++ b/board/safety_declarations.h @@ -1,3 +1,4 @@ +#define GET_BIT(msg, b) (((msg)->data[(int)((b) / 8)] >> ((b) % 8)) & 0x1) #define GET_BYTE(msg, b) ((msg)->data[(b)]) #define GET_BYTES_04(msg) ((msg)->data[0] | ((msg)->data[1] << 8) | ((msg)->data[2] << 16) | ((msg)->data[3] << 24)) #define GET_BYTES_48(msg) ((msg)->data[4] | ((msg)->data[5] << 8) | ((msg)->data[6] << 16) | ((msg)->data[7] << 24)) @@ -110,6 +111,7 @@ bool brake_pressed_prev = false; bool cruise_engaged_prev = false; float vehicle_speed = 0; bool vehicle_moving = false; +bool acc_main_on = false; // referred to as "ACC off" in ISO 15622:2018 // for safety modes with torque steering control int desired_torque_last = 0; // last desired steer torque diff --git a/python/__init__.py b/python/__init__.py index 33ed239c..97e37a2e 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -231,6 +231,10 @@ class Panda(object): FLAG_HONDA_ALT_BRAKE = 1 FLAG_HONDA_BOSCH_LONG = 2 + FLAG_HONDA_NIDEC_ALT = 4 + + FLAG_HYUNDAI_EV_GAS = 1 + FLAG_HYUNDAI_HYBRID_GAS = 2 FLAG_HYUNDAI_LONG = 4 def __init__(self, serial=None, claim=True): diff --git a/tests/safety/common.py b/tests/safety/common.py index aa7ec61a..bcf4363d 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -424,7 +424,8 @@ class PandaSafetyTest(PandaSafetyTestBase): tx = getattr(getattr(test, attr), "TX_MSGS") if tx is not None: # TODO: Temporary, should be fixed in panda firmware, safety_honda.h - if attr in ['TestHondaBoschLongGiraffeSafety', 'TestHondaNidecSafety']: + if attr.startswith('TestHonda'): + # exceptions for common msgs across different hondas tx = list(filter(lambda m: m[0] not in [0x1FA, 0x30C], tx)) all_tx.append(list([m[0], m[1], attr[4:]] for m in tx)) diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py index 1f8152e3..2bae48d5 100755 --- a/tests/safety/test_honda.py +++ b/tests/safety/test_honda.py @@ -26,6 +26,7 @@ class TestHondaSafety(common.PandaSafetyTest): cnt_gas = 0 cnt_button = 0 cnt_brake = 0 + cnt_acc_state = 0 @classmethod def setUpClass(cls): @@ -52,7 +53,12 @@ class TestHondaSafety(common.PandaSafetyTest): self.__class__.cnt_speed += 1 return self.packer.make_can_msg_panda("ENGINE_DATA", self.PT_BUS, values) - def _button_msg(self, buttons): + def _acc_state_msg(self, main_on): + values = {"MAIN_ON": main_on, "COUNTER": self.cnt_acc_state % 4} + self.__class__.cnt_acc_state += 1 + return self.packer.make_can_msg_panda("SCM_FEEDBACK", self.PT_BUS, values) + + def _button_msg(self, buttons, main_on=False): values = {"CRUISE_BUTTONS": buttons, "COUNTER": self.cnt_button % 4} self.__class__.cnt_button += 1 return self.packer.make_can_msg_panda("SCM_BUTTONS", self.PT_BUS, values) @@ -75,19 +81,35 @@ class TestHondaSafety(common.PandaSafetyTest): # must be implemented when inherited raise NotImplementedError + def test_buttons_with_main_off(self): + for btn in [Btn.SET, Btn.RESUME, Btn.CANCEL]: + self.safety.set_controls_allowed(1) + self._rx(self._acc_state_msg(False)) + self._rx(self._button_msg(btn, main_on=False)) + self.assertFalse(self.safety.get_controls_allowed()) + def test_resume_button(self): + self._rx(self._acc_state_msg(True)) self.safety.set_controls_allowed(0) - self._rx(self._button_msg(Btn.RESUME)) + self._rx(self._button_msg(Btn.RESUME, main_on=True)) self.assertTrue(self.safety.get_controls_allowed()) def test_set_button(self): + self._rx(self._acc_state_msg(True)) self.safety.set_controls_allowed(0) - self._rx(self._button_msg(Btn.SET)) + self._rx(self._button_msg(Btn.SET, main_on=True)) self.assertTrue(self.safety.get_controls_allowed()) def test_cancel_button(self): self.safety.set_controls_allowed(1) - self._rx(self._button_msg(Btn.CANCEL)) + self._rx(self._button_msg(Btn.CANCEL, main_on=True)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_disengage_on_main(self): + self.safety.set_controls_allowed(1) + self._rx(self._acc_state_msg(True)) + self.assertTrue(self.safety.get_controls_allowed()) + self._rx(self._acc_state_msg(False)) self.assertFalse(self.safety.get_controls_allowed()) def test_disengage_on_brake(self): @@ -106,8 +128,6 @@ class TestHondaSafety(common.PandaSafetyTest): # checksum checks for msg in ["btn", "gas", "speed"]: self.safety.set_controls_allowed(1) - # TODO: add this coverage back by re-running all tests with the acura dbc - # to_push = self._button_msg(Btn.SET, 0x1A6) # only in Honda_NIDEC if msg == "btn": to_push = self._button_msg(Btn.SET) if msg == "gas": @@ -146,7 +166,7 @@ class TestHondaSafety(common.PandaSafetyTest): self._rx(self._button_msg(Btn.SET)) self._rx(self._speed_msg(0)) self._rx(self._gas_msg(0)) - self._rx(self._button_msg(Btn.SET)) + self._rx(self._button_msg(Btn.SET, main_on=True)) self.assertTrue(self.safety.get_controls_allowed()) def test_tx_hook_on_pedal_pressed(self): @@ -206,7 +226,7 @@ class TestHondaNidecSafety(TestHondaSafety, common.InterceptorSafetyTest): def _interceptor_msg(self, gas, addr): to_send = make_msg(0, addr, 6) gas2 = gas * 2 - to_send[0].data[0] = (gas & 0xFF00) >> 8 + to_send[0].data[0] = (gas & 0xFF00) >> 8 to_send[0].data[1] = gas & 0xFF to_send[0].data[2] = (gas2 & 0xFF00) >> 8 to_send[0].data[3] = gas2 & 0xFF @@ -266,6 +286,24 @@ class TestHondaNidecSafety(TestHondaSafety, common.InterceptorSafetyTest): self.safety.set_gas_interceptor_detected(False) +class TestHondaNidecAltSafety(TestHondaNidecSafety, common.InterceptorSafetyTest): + def setUp(self): + self.packer = CANPackerPanda("acura_ilx_2016_can_generated") + self.safety = libpandasafety_py.libpandasafety + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, Panda.FLAG_HONDA_NIDEC_ALT) + self.safety.init_tests_honda() + + def _acc_state_msg(self, main_on): + values = {"MAIN_ON": main_on, "COUNTER": self.cnt_acc_state % 4} + self.__class__.cnt_acc_state += 1 + return self.packer.make_can_msg_panda("SCM_BUTTONS", self.PT_BUS, values) + + def _button_msg(self, buttons, main_on=False): + values = {"CRUISE_BUTTONS": buttons, "MAIN_ON": main_on, "COUNTER": self.cnt_button % 4} + self.__class__.cnt_button += 1 + return self.packer.make_can_msg_panda("SCM_BUTTONS", self.PT_BUS, values) + + class TestHondaBoschSafety(TestHondaSafety): STANDSTILL_THRESHOLD = 0 RELAY_MALFUNCTION_ADDR = 0xE4