Squashed 'panda/' changes from 8843af7de..9881e6118

9881e6118 Panda for Mazda (#165)
9a15d2f5b fix version newline
a8ed7d219 add subaru outback/legacy to subaru safety (#259)
bdeb1c953 mazda is #12
14ea4d2e0 merge safety gm in a single file
bf1ef875e Add GM passive safety mode (#266)
c131fffae fix canflash for pedal (#267)
3397b1527 only allow bootloader entry on debug builds
d68356b92 Honda Nidec: fwd stock AEB (#257)
6f532c6d5 Black panda Jenkins (#256)
d68508c79 Gpio race condition fix (#263)
d69d05fc0 Fixed pedal not initializing (#262)
36067e01c Honda safety: fixed incorrect brake decoding. Due to the specific limit of 255, this change does not affect the safety behavior

git-subtree-dir: panda
git-subtree-split: 9881e61184ad0417e9e080767f09585a9c777621

old-commit-hash: 876256a268
commatwo_master
Vehicle Researcher 6 years ago
parent 229b51e5d0
commit 4efb208892
  1. 2
      .gitignore
  2. 2
      VERSION
  3. 10
      board/board.h
  4. 4
      board/board_declarations.h
  5. 34
      board/boards/black.h
  6. 7
      board/boards/white.h
  7. 2
      board/bootstub.c
  8. 25
      board/drivers/can.h
  9. 10
      board/drivers/llgpio.h
  10. 24
      board/drivers/uart.h
  11. 6
      board/gpio.h
  12. 26
      board/libc.h
  13. 61
      board/main.c
  14. 13
      board/pedal/main.c
  15. 7
      board/power_saving.h
  16. 5
      board/safety.h
  17. 23
      board/safety/safety_gm.h
  18. 47
      board/safety/safety_honda.h
  19. 169
      board/safety/safety_mazda.h
  20. 18
      board/safety/safety_subaru.h
  21. 4
      board/spi_flasher.h
  22. 14
      python/__init__.py
  23. 16
      tests/automated/1_program.py
  24. 91
      tests/automated/2_usb_to_can.py
  25. 44
      tests/automated/3_wifi.py
  26. 36
      tests/automated/4_wifi_functionality.py
  27. 10
      tests/automated/5_wifi_udp.py
  28. 132
      tests/automated/6_two_panda.py
  29. 164
      tests/automated/helpers.py
  30. 105
      tests/black_loopback_test.py
  31. 169
      tests/black_white_loopback_test.py
  32. 175
      tests/black_white_relay_endurance.py
  33. 145
      tests/black_white_relay_test.py
  34. 53
      tests/debug_console.py
  35. 19
      tests/health_test.py
  36. 3
      tests/safety/libpandasafety_py.py
  37. 11
      tests/safety/test.c
  38. 69
      tests/safety/test_honda.py

2
.gitignore vendored

@ -4,6 +4,7 @@
*.o *.o
*.so *.so
*.d *.d
*.dump
a.out a.out
*~ *~
.#* .#*
@ -12,4 +13,5 @@ pandacan.egg-info/
board/obj/ board/obj/
examples/output.csv examples/output.csv
.DS_Store .DS_Store
.vscode
nosetests.xml nosetests.xml

@ -1 +1 @@
v1.4.3 v1.4.7

@ -48,8 +48,12 @@ void detect_configuration(void) {
has_external_debug_serial = detect_with_pull(GPIOA, 3, PULL_DOWN); has_external_debug_serial = detect_with_pull(GPIOA, 3, PULL_DOWN);
#ifdef PANDA #ifdef PANDA
// check if the ESP is trying to put me in boot mode if(hw_type == HW_TYPE_WHITE_PANDA) {
is_entering_bootmode = !detect_with_pull(GPIOB, 0, PULL_UP); // check if the ESP is trying to put me in boot mode
is_entering_bootmode = !detect_with_pull(GPIOB, 0, PULL_UP);
} else {
is_entering_bootmode = 0;
}
#else #else
is_entering_bootmode = 0; is_entering_bootmode = 0;
#endif #endif
@ -58,4 +62,4 @@ void detect_configuration(void) {
// ///// Board functions ///// // // ///// Board functions ///// //
bool board_has_gps(void) { bool board_has_gps(void) {
return ((hw_type == HW_TYPE_GREY_PANDA) || (hw_type == HW_TYPE_BLACK_PANDA)); return ((hw_type == HW_TYPE_GREY_PANDA) || (hw_type == HW_TYPE_BLACK_PANDA));
} }

@ -24,7 +24,7 @@ struct board {
}; };
// ******************* Definitions ******************** // ******************* Definitions ********************
// These should match the enum in cereal/log.capnp // These should match the enums in cereal/log.capnp and __init__.py
#define HW_TYPE_UNKNOWN 0U #define HW_TYPE_UNKNOWN 0U
#define HW_TYPE_WHITE_PANDA 1U #define HW_TYPE_WHITE_PANDA 1U
#define HW_TYPE_GREY_PANDA 2U #define HW_TYPE_GREY_PANDA 2U
@ -54,4 +54,4 @@ struct board {
#define CAN_MODE_OBD_CAN2 3U #define CAN_MODE_OBD_CAN2 3U
// ********************* Globals ********************** // ********************* Globals **********************
uint8_t usb_power_mode = USB_POWER_NONE; uint8_t usb_power_mode = USB_POWER_NONE;

@ -23,8 +23,9 @@ void black_enable_can_transciever(uint8_t transciever, bool enabled) {
} }
void black_enable_can_transcievers(bool enabled) { void black_enable_can_transcievers(bool enabled) {
for(uint8_t i=1; i<=4U; i++) for(uint8_t i=1U; i<=4U; i++){
black_enable_can_transciever(i, enabled); black_enable_can_transciever(i, enabled);
}
} }
void black_set_led(uint8_t color, bool enabled) { void black_set_led(uint8_t color, bool enabled) {
@ -43,26 +44,41 @@ void black_set_led(uint8_t color, bool enabled) {
} }
} }
void black_set_usb_power_mode(uint8_t mode){ void black_set_gps_load_switch(bool enabled) {
set_gpio_output(GPIOC, 12, enabled);
}
void black_set_usb_load_switch(bool enabled) {
set_gpio_output(GPIOB, 1, !enabled);
}
void black_set_usb_power_mode(uint8_t mode) {
usb_power_mode = mode; usb_power_mode = mode;
puts("Trying to set USB power mode on black panda. This is not supported.\n"); if (mode == USB_POWER_NONE) {
black_set_usb_load_switch(false);
} else {
black_set_usb_load_switch(true);
}
} }
void black_set_esp_gps_mode(uint8_t mode) { void black_set_esp_gps_mode(uint8_t mode) {
switch (mode) { switch (mode) {
case ESP_GPS_DISABLED: case ESP_GPS_DISABLED:
// ESP OFF // GPS OFF
set_gpio_output(GPIOC, 14, 0); set_gpio_output(GPIOC, 14, 0);
set_gpio_output(GPIOC, 5, 0); set_gpio_output(GPIOC, 5, 0);
black_set_gps_load_switch(false);
break; break;
case ESP_GPS_ENABLED: case ESP_GPS_ENABLED:
// ESP ON // GPS ON
set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 14, 1);
set_gpio_output(GPIOC, 5, 1); set_gpio_output(GPIOC, 5, 1);
black_set_gps_load_switch(true);
break; break;
case ESP_GPS_BOOTMODE: case ESP_GPS_BOOTMODE:
set_gpio_output(GPIOC, 14, 1); set_gpio_output(GPIOC, 14, 1);
set_gpio_output(GPIOC, 5, 0); set_gpio_output(GPIOC, 5, 0);
black_set_gps_load_switch(true);
break; break;
default: default:
puts("Invalid ESP/GPS mode\n"); puts("Invalid ESP/GPS mode\n");
@ -132,9 +148,11 @@ void black_init(void) {
// C8: FAN aka TIM3_CH3 // C8: FAN aka TIM3_CH3
set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3);
// C12: GPS load switch. Turn on permanently for now // Turn on GPS load switch.
set_gpio_output(GPIOC, 12, true); black_set_gps_load_switch(true);
//set_gpio_output(GPIOC, 12, false); //TODO: stupid inverted switch on prototype
// Turn on USB load switch.
black_set_usb_load_switch(true);
// Initialize harness // Initialize harness
harness_init(); harness_init();

@ -285,6 +285,13 @@ void white_init(void) {
// Set normal CAN mode // Set normal CAN mode
white_set_can_mode(CAN_MODE_NORMAL); white_set_can_mode(CAN_MODE_NORMAL);
// Setup ignition interrupts
SYSCFG->EXTICR[1] = SYSCFG_EXTICR1_EXTI1_PA;
EXTI->IMR |= (1U << 1);
EXTI->RTSR |= (1U << 1);
EXTI->FTSR |= (1U << 1);
NVIC_EnableIRQ(EXTI1_IRQn);
} }
const harness_configuration white_harness_config = { const harness_configuration white_harness_config = {

@ -63,7 +63,7 @@ extern void *_app_start[];
// BOUNTY: $200 coupon on shop.comma.ai or $100 check. // BOUNTY: $200 coupon on shop.comma.ai or $100 check.
int main(void) { int main(void) {
__disable_irq(); disable_interrupts();
clock_init(); clock_init();
detect_configuration(); detect_configuration();
detect_board_type(); detect_board_type();

@ -62,7 +62,7 @@ int can_overflow_cnt = 0;
bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) { bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) {
bool ret = 0; bool ret = 0;
enter_critical_section(); ENTER_CRITICAL();
if (q->w_ptr != q->r_ptr) { if (q->w_ptr != q->r_ptr) {
*elem = q->elems[q->r_ptr]; *elem = q->elems[q->r_ptr];
if ((q->r_ptr + 1U) == q->fifo_size) { if ((q->r_ptr + 1U) == q->fifo_size) {
@ -72,7 +72,7 @@ bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) {
} }
ret = 1; ret = 1;
} }
exit_critical_section(); EXIT_CRITICAL();
return ret; return ret;
} }
@ -81,7 +81,7 @@ bool can_push(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) {
bool ret = false; bool ret = false;
uint32_t next_w_ptr; uint32_t next_w_ptr;
enter_critical_section(); ENTER_CRITICAL();
if ((q->w_ptr + 1U) == q->fifo_size) { if ((q->w_ptr + 1U) == q->fifo_size) {
next_w_ptr = 0; next_w_ptr = 0;
} else { } else {
@ -92,7 +92,7 @@ bool can_push(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) {
q->w_ptr = next_w_ptr; q->w_ptr = next_w_ptr;
ret = true; ret = true;
} }
exit_critical_section(); EXIT_CRITICAL();
if (!ret) { if (!ret) {
can_overflow_cnt++; can_overflow_cnt++;
#ifdef DEBUG #ifdef DEBUG
@ -103,10 +103,10 @@ bool can_push(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) {
} }
void can_clear(can_ring *q) { void can_clear(can_ring *q) {
enter_critical_section(); ENTER_CRITICAL();
q->w_ptr = 0; q->w_ptr = 0;
q->r_ptr = 0; q->r_ptr = 0;
exit_critical_section(); EXIT_CRITICAL();
} }
// assign CAN numbering // assign CAN numbering
@ -124,7 +124,7 @@ uint8_t bus_lookup[] = {0,1,2};
uint8_t can_num_lookup[] = {0,1,2,-1}; uint8_t can_num_lookup[] = {0,1,2,-1};
int8_t can_forwarding[] = {-1,-1,-1,-1}; int8_t can_forwarding[] = {-1,-1,-1,-1};
uint32_t can_speed[] = {5000, 5000, 5000, 333}; uint32_t can_speed[] = {5000, 5000, 5000, 333};
#define CAN_MAX 3 #define CAN_MAX 3U
#define CANIF_FROM_CAN_NUM(num) (cans[num]) #define CANIF_FROM_CAN_NUM(num) (cans[num])
#define CAN_NUM_FROM_CANIF(CAN) ((CAN)==CAN1 ? 0 : ((CAN) == CAN2 ? 1 : 2)) #define CAN_NUM_FROM_CANIF(CAN) ((CAN)==CAN1 ? 0 : ((CAN) == CAN2 ? 1 : 2))
@ -158,9 +158,10 @@ void can_init(uint8_t can_number) {
} }
void can_init_all(void) { void can_init_all(void) {
for (int i=0; i < CAN_MAX; i++) { for (uint8_t i=0U; i < CAN_MAX; i++) {
can_init(i); can_init(i);
} }
current_board->enable_can_transcievers(true);
} }
void can_flip_buses(uint8_t bus1, uint8_t bus2){ void can_flip_buses(uint8_t bus1, uint8_t bus2){
@ -248,7 +249,7 @@ void can_set_obd(uint8_t harness_orientation, bool obd){
// CAN error // CAN error
void can_sce(CAN_TypeDef *CAN) { void can_sce(CAN_TypeDef *CAN) {
enter_critical_section(); ENTER_CRITICAL();
#ifdef DEBUG #ifdef DEBUG
if (CAN==CAN1) puts("CAN1: "); if (CAN==CAN1) puts("CAN1: ");
@ -271,7 +272,7 @@ void can_sce(CAN_TypeDef *CAN) {
can_err_cnt += 1; can_err_cnt += 1;
llcan_clear_send(CAN); llcan_clear_send(CAN);
exit_critical_section(); EXIT_CRITICAL();
} }
// ***************************** CAN ***************************** // ***************************** CAN *****************************
@ -279,7 +280,7 @@ void can_sce(CAN_TypeDef *CAN) {
void process_can(uint8_t can_number) { void process_can(uint8_t can_number) {
if (can_number != 0xffU) { if (can_number != 0xffU) {
enter_critical_section(); ENTER_CRITICAL();
CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number);
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number);
@ -327,7 +328,7 @@ void process_can(uint8_t can_number) {
} }
} }
exit_critical_section(); EXIT_CRITICAL();
} }
} }

@ -11,42 +11,52 @@
#define OUTPUT_TYPE_OPEN_DRAIN 1U #define OUTPUT_TYPE_OPEN_DRAIN 1U
void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) {
ENTER_CRITICAL();
uint32_t tmp = GPIO->MODER; uint32_t tmp = GPIO->MODER;
tmp &= ~(3U << (pin * 2U)); tmp &= ~(3U << (pin * 2U));
tmp |= (mode << (pin * 2U)); tmp |= (mode << (pin * 2U));
GPIO->MODER = tmp; GPIO->MODER = tmp;
EXIT_CRITICAL();
} }
void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) { void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) {
ENTER_CRITICAL();
if (enabled) { if (enabled) {
GPIO->ODR |= (1U << pin); GPIO->ODR |= (1U << pin);
} else { } else {
GPIO->ODR &= ~(1U << pin); GPIO->ODR &= ~(1U << pin);
} }
set_gpio_mode(GPIO, pin, MODE_OUTPUT); set_gpio_mode(GPIO, pin, MODE_OUTPUT);
EXIT_CRITICAL();
} }
void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){ void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){
ENTER_CRITICAL();
if(output_type == OUTPUT_TYPE_OPEN_DRAIN) { if(output_type == OUTPUT_TYPE_OPEN_DRAIN) {
GPIO->OTYPER |= (1U << pin); GPIO->OTYPER |= (1U << pin);
} else { } else {
GPIO->OTYPER &= ~(1U << pin); GPIO->OTYPER &= ~(1U << pin);
} }
EXIT_CRITICAL();
} }
void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) {
ENTER_CRITICAL();
uint32_t tmp = GPIO->AFR[pin >> 3U]; uint32_t tmp = GPIO->AFR[pin >> 3U];
tmp &= ~(0xFU << ((pin & 7U) * 4U)); tmp &= ~(0xFU << ((pin & 7U) * 4U));
tmp |= mode << ((pin & 7U) * 4U); tmp |= mode << ((pin & 7U) * 4U);
GPIO->AFR[pin >> 3] = tmp; GPIO->AFR[pin >> 3] = tmp;
set_gpio_mode(GPIO, pin, MODE_ALTERNATE); set_gpio_mode(GPIO, pin, MODE_ALTERNATE);
EXIT_CRITICAL();
} }
void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) {
ENTER_CRITICAL();
uint32_t tmp = GPIO->PUPDR; uint32_t tmp = GPIO->PUPDR;
tmp &= ~(3U << (pin * 2U)); tmp &= ~(3U << (pin * 2U));
tmp |= (mode << (pin * 2U)); tmp |= (mode << (pin * 2U));
GPIO->PUPDR = tmp; GPIO->PUPDR = tmp;
EXIT_CRITICAL();
} }
int get_gpio_input(GPIO_TypeDef *GPIO, unsigned int pin) { int get_gpio_input(GPIO_TypeDef *GPIO, unsigned int pin) {

@ -74,7 +74,7 @@ uart_ring *get_ring_by_number(int a) {
// ***************************** serial port ***************************** // ***************************** serial port *****************************
void uart_ring_process(uart_ring *q) { void uart_ring_process(uart_ring *q) {
enter_critical_section(); ENTER_CRITICAL();
// TODO: check if external serial is connected // TODO: check if external serial is connected
int sr = q->uart->SR; int sr = q->uart->SR;
@ -108,7 +108,7 @@ void uart_ring_process(uart_ring *q) {
// set dropped packet flag? // set dropped packet flag?
} }
exit_critical_section(); EXIT_CRITICAL();
} }
// interrupt boilerplate // interrupt boilerplate
@ -121,13 +121,13 @@ void UART5_IRQHandler(void) { uart_ring_process(&lin1_ring); }
bool getc(uart_ring *q, char *elem) { bool getc(uart_ring *q, char *elem) {
bool ret = false; bool ret = false;
enter_critical_section(); ENTER_CRITICAL();
if (q->w_ptr_rx != q->r_ptr_rx) { if (q->w_ptr_rx != q->r_ptr_rx) {
if (elem != NULL) *elem = q->elems_rx[q->r_ptr_rx]; if (elem != NULL) *elem = q->elems_rx[q->r_ptr_rx];
q->r_ptr_rx = (q->r_ptr_rx + 1U) % FIFO_SIZE; q->r_ptr_rx = (q->r_ptr_rx + 1U) % FIFO_SIZE;
ret = true; ret = true;
} }
exit_critical_section(); EXIT_CRITICAL();
return ret; return ret;
} }
@ -136,14 +136,14 @@ bool injectc(uart_ring *q, char elem) {
int ret = false; int ret = false;
uint16_t next_w_ptr; uint16_t next_w_ptr;
enter_critical_section(); ENTER_CRITICAL();
next_w_ptr = (q->w_ptr_rx + 1U) % FIFO_SIZE; next_w_ptr = (q->w_ptr_rx + 1U) % FIFO_SIZE;
if (next_w_ptr != q->r_ptr_rx) { if (next_w_ptr != q->r_ptr_rx) {
q->elems_rx[q->w_ptr_rx] = elem; q->elems_rx[q->w_ptr_rx] = elem;
q->w_ptr_rx = next_w_ptr; q->w_ptr_rx = next_w_ptr;
ret = true; ret = true;
} }
exit_critical_section(); EXIT_CRITICAL();
return ret; return ret;
} }
@ -152,14 +152,14 @@ bool putc(uart_ring *q, char elem) {
bool ret = false; bool ret = false;
uint16_t next_w_ptr; uint16_t next_w_ptr;
enter_critical_section(); ENTER_CRITICAL();
next_w_ptr = (q->w_ptr_tx + 1U) % FIFO_SIZE; next_w_ptr = (q->w_ptr_tx + 1U) % FIFO_SIZE;
if (next_w_ptr != q->r_ptr_tx) { if (next_w_ptr != q->r_ptr_tx) {
q->elems_tx[q->w_ptr_tx] = elem; q->elems_tx[q->w_ptr_tx] = elem;
q->w_ptr_tx = next_w_ptr; q->w_ptr_tx = next_w_ptr;
ret = true; ret = true;
} }
exit_critical_section(); EXIT_CRITICAL();
uart_ring_process(q); uart_ring_process(q);
@ -185,12 +185,12 @@ void uart_send_break(uart_ring *u) {
} }
void clear_uart_buff(uart_ring *q) { void clear_uart_buff(uart_ring *q) {
enter_critical_section(); ENTER_CRITICAL();
q->w_ptr_tx = 0; q->w_ptr_tx = 0;
q->r_ptr_tx = 0; q->r_ptr_tx = 0;
q->w_ptr_rx = 0; q->w_ptr_rx = 0;
q->r_ptr_rx = 0; q->r_ptr_rx = 0;
exit_critical_section(); EXIT_CRITICAL();
} }
// ***************************** start UART code ***************************** // ***************************** start UART code *****************************
@ -215,7 +215,7 @@ char usart1_dma[USART1_DMA_LEN];
void uart_dma_drain(void) { void uart_dma_drain(void) {
uart_ring *q = &esp_ring; uart_ring *q = &esp_ring;
enter_critical_section(); ENTER_CRITICAL();
if ((DMA2->HISR & DMA_HISR_TCIF5) || (DMA2->HISR & DMA_HISR_HTIF5) || (DMA2_Stream5->NDTR != USART1_DMA_LEN)) { if ((DMA2->HISR & DMA_HISR_TCIF5) || (DMA2->HISR & DMA_HISR_HTIF5) || (DMA2_Stream5->NDTR != USART1_DMA_LEN)) {
// disable DMA // disable DMA
@ -245,7 +245,7 @@ void uart_dma_drain(void) {
q->uart->CR3 |= USART_CR3_DMAR; q->uart->CR3 |= USART_CR3_DMAR;
} }
exit_critical_section(); EXIT_CRITICAL();
} }
void DMA2_Stream5_IRQHandler(void) { void DMA2_Stream5_IRQHandler(void) {

@ -20,6 +20,12 @@ void jump_to_bootloader(void) {
} }
void early(void) { void early(void) {
// Reset global critical depth
global_critical_depth = 0;
// neccesary for DFU flashing on a non-power cycled white panda
enable_interrupts();
// after it's been in the bootloader, things are initted differently, so we reset // after it's been in the bootloader, things are initted differently, so we reset
if ((enter_bootloader_mode != BOOT_NORMAL) && if ((enter_bootloader_mode != BOOT_NORMAL) &&
(enter_bootloader_mode != ENTER_BOOTLOADER_MAGIC) && (enter_bootloader_mode != ENTER_BOOTLOADER_MAGIC) &&

@ -42,24 +42,26 @@ int memcmp(const void * ptr1, const void * ptr2, unsigned int num) {
// ********************* IRQ helpers ********************* // ********************* IRQ helpers *********************
int interrupts_enabled = 0; volatile bool interrupts_enabled = false;
void enable_interrupts(void) { void enable_interrupts(void) {
interrupts_enabled = 1; interrupts_enabled = true;
__enable_irq(); __enable_irq();
} }
int critical_depth = 0; void disable_interrupts(void) {
void enter_critical_section(void) { interrupts_enabled = false;
__disable_irq(); __disable_irq();
// this is safe because interrupts are disabled
critical_depth += 1;
} }
void exit_critical_section(void) { uint8_t global_critical_depth = 0U;
// this is safe because interrupts are disabled #define ENTER_CRITICAL() \
critical_depth -= 1; __disable_irq(); \
if ((critical_depth == 0) && interrupts_enabled) { global_critical_depth += 1U;
__enable_irq();
#define EXIT_CRITICAL() \
global_critical_depth -= 1U; \
if ((global_critical_depth == 0U) && interrupts_enabled) { \
__enable_irq(); \
} }
}

@ -78,9 +78,11 @@ void started_interrupt_handler(uint8_t interrupt_line) {
// jenky debounce // jenky debounce
delay(100000); delay(100000);
// set power savings mode here // set power savings mode here if on EON build
int power_save_state = current_board->check_ignition() ? POWER_SAVE_STATUS_DISABLED : POWER_SAVE_STATUS_ENABLED; #ifdef EON
set_power_save_state(power_save_state); int power_save_state = current_board->check_ignition() ? POWER_SAVE_STATUS_DISABLED : POWER_SAVE_STATUS_ENABLED;
set_power_save_state(power_save_state);
#endif
} }
EXTI->PR = (1U << interrupt_line); EXTI->PR = (1U << interrupt_line);
} }
@ -100,14 +102,6 @@ void EXTI3_IRQHandler(void) {
started_interrupt_handler(3); started_interrupt_handler(3);
} }
void started_interrupt_init(void) {
SYSCFG->EXTICR[1] = SYSCFG_EXTICR1_EXTI1_PA;
EXTI->IMR |= (1U << 1);
EXTI->RTSR |= (1U << 1);
EXTI->FTSR |= (1U << 1);
NVIC_EnableIRQ(EXTI1_IRQn);
}
// ****************************** safety mode ****************************** // ****************************** safety mode ******************************
// this is the only way to leave silent mode // this is the only way to leave silent mode
@ -116,30 +110,29 @@ void set_safety_mode(uint16_t mode, int16_t param) {
if (err == -1) { if (err == -1) {
puts("Error: safety set mode failed\n"); puts("Error: safety set mode failed\n");
} else { } else {
if (mode == SAFETY_NOOUTPUT) {
can_silent = ALL_CAN_SILENT;
} else {
can_silent = ALL_CAN_LIVE;
}
switch (mode) { switch (mode) {
case SAFETY_NOOUTPUT: case SAFETY_NOOUTPUT:
set_intercept_relay(false); set_intercept_relay(false);
if(hw_type == HW_TYPE_BLACK_PANDA){ if(hw_type == HW_TYPE_BLACK_PANDA){
current_board->set_can_mode(CAN_MODE_NORMAL); current_board->set_can_mode(CAN_MODE_NORMAL);
} }
can_silent = ALL_CAN_SILENT;
break; break;
case SAFETY_ELM327: case SAFETY_ELM327:
set_intercept_relay(false); set_intercept_relay(false);
heartbeat_counter = 0U;
if(hw_type == HW_TYPE_BLACK_PANDA){ if(hw_type == HW_TYPE_BLACK_PANDA){
current_board->set_can_mode(CAN_MODE_OBD_CAN2); current_board->set_can_mode(CAN_MODE_OBD_CAN2);
} }
can_silent = ALL_CAN_LIVE;
break; break;
default: default:
set_intercept_relay(true); set_intercept_relay(true);
heartbeat_counter = 0U;
if(hw_type == HW_TYPE_BLACK_PANDA){ if(hw_type == HW_TYPE_BLACK_PANDA){
current_board->set_can_mode(CAN_MODE_NORMAL); current_board->set_can_mode(CAN_MODE_NORMAL);
} }
can_silent = ALL_CAN_LIVE;
break; break;
} }
if (safety_ignition_hook() != -1) { if (safety_ignition_hook() != -1) {
@ -289,11 +282,14 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired)
// so it's blocked over wifi // so it's blocked over wifi
switch (setup->b.wValue.w) { switch (setup->b.wValue.w) {
case 0: case 0:
if (hardwired) { // only allow bootloader entry on debug builds
puts("-> entering bootloader\n"); #ifdef ALLOW_DEBUG
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; if (hardwired) {
NVIC_SystemReset(); puts("-> entering bootloader\n");
} enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
NVIC_SystemReset();
}
#endif
break; break;
case 1: case 1:
puts("-> entering softloader\n"); puts("-> entering softloader\n");
@ -464,7 +460,10 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, bool hardwired)
break; break;
// **** 0xe6: set USB power // **** 0xe6: set USB power
case 0xe6: case 0xe6:
if (setup->b.wValue.w == 1U) { if (setup->b.wValue.w == 0U) {
puts("user setting NONE mode\n");
current_board->set_usb_power_mode(USB_POWER_NONE);
} else if (setup->b.wValue.w == 1U) {
puts("user setting CDP mode\n"); puts("user setting CDP mode\n");
current_board->set_usb_power_mode(USB_POWER_CDP); current_board->set_usb_power_mode(USB_POWER_CDP);
} else if (setup->b.wValue.w == 2U) { } else if (setup->b.wValue.w == 2U) {
@ -610,11 +609,10 @@ void TIM3_IRQHandler(void) {
pending_can_live = 0; pending_can_live = 0;
} }
#ifdef DEBUG #ifdef DEBUG
//TODO: re-enable puts("** blink ");
//puts("** blink "); puth(can_rx_q.r_ptr); puts(" "); puth(can_rx_q.w_ptr); puts(" ");
//puth(can_rx_q.r_ptr); puts(" "); puth(can_rx_q.w_ptr); puts(" "); puth(can_tx1_q.r_ptr); puts(" "); puth(can_tx1_q.w_ptr); puts(" ");
//puth(can_tx1_q.r_ptr); puts(" "); puth(can_tx1_q.w_ptr); puts(" "); puth(can_tx2_q.r_ptr); puts(" "); puth(can_tx2_q.w_ptr); puts("\n");
//puth(can_tx2_q.r_ptr); puts(" "); puth(can_tx2_q.w_ptr); puts("\n");
#endif #endif
// set green LED to be controls allowed // set green LED to be controls allowed
@ -645,7 +643,7 @@ void TIM3_IRQHandler(void) {
int main(void) { int main(void) {
// shouldn't have interrupts here, but just in case // shouldn't have interrupts here, but just in case
__disable_irq(); disable_interrupts();
// init early devices // init early devices
clock_init(); clock_init();
@ -730,11 +728,6 @@ int main(void) {
/*if (current_board->check_ignition()) { /*if (current_board->check_ignition()) {
set_power_save_state(POWER_SAVE_STATUS_ENABLED); set_power_save_state(POWER_SAVE_STATUS_ENABLED);
}*/ }*/
if (hw_type != HW_TYPE_BLACK_PANDA) {
// interrupt on started line
started_interrupt_init();
}
#endif #endif
// 48mhz / 65536 ~= 732 / 732 = 1 // 48mhz / 65536 ~= 732 / 732 = 1

@ -55,18 +55,18 @@ void debug_ring_callback(uart_ring *ring) {
} }
} }
int usb_cb_ep1_in(uint8_t *usbdata, int len, bool hardwired) { int usb_cb_ep1_in(void *usbdata, int len, bool hardwired) {
UNUSED(usbdata); UNUSED(usbdata);
UNUSED(len); UNUSED(len);
UNUSED(hardwired); UNUSED(hardwired);
return 0; return 0;
} }
void usb_cb_ep2_out(uint8_t *usbdata, int len, bool hardwired) { void usb_cb_ep2_out(void *usbdata, int len, bool hardwired) {
UNUSED(usbdata); UNUSED(usbdata);
UNUSED(len); UNUSED(len);
UNUSED(hardwired); UNUSED(hardwired);
} }
void usb_cb_ep3_out(uint8_t *usbdata, int len, bool hardwired) { void usb_cb_ep3_out(void *usbdata, int len, bool hardwired) {
UNUSED(usbdata); UNUSED(usbdata);
UNUSED(len); UNUSED(len);
UNUSED(hardwired); UNUSED(hardwired);
@ -299,7 +299,7 @@ void pedal(void) {
} }
int main(void) { int main(void) {
__disable_irq(); disable_interrupts();
// init devices // init devices
clock_init(); clock_init();
@ -307,6 +307,9 @@ int main(void) {
detect_configuration(); detect_configuration();
detect_board_type(); detect_board_type();
// init board
current_board->init();
#ifdef PEDAL_USB #ifdef PEDAL_USB
// enable USB // enable USB
usb_init(); usb_init();
@ -331,7 +334,7 @@ int main(void) {
watchdog_init(); watchdog_init();
puts("**** INTERRUPTS ON ****\n"); puts("**** INTERRUPTS ON ****\n");
__enable_irq(); enable_interrupts();
// main pedal loop // main pedal loop
while (1) { while (1) {

@ -28,6 +28,13 @@ void set_power_save_state(int state) {
// Switch CAN transcievers // Switch CAN transcievers
current_board->enable_can_transcievers(enable); current_board->enable_can_transcievers(enable);
// Switch EPS/GPS
if (enable) {
current_board->set_esp_gps_mode(ESP_GPS_ENABLED);
} else {
current_board->set_esp_gps_mode(ESP_GPS_DISABLED);
}
if(hw_type != HW_TYPE_BLACK_PANDA){ if(hw_type != HW_TYPE_BLACK_PANDA){
// turn on GMLAN // turn on GMLAN
set_gpio_output(GPIOB, 14, enable); set_gpio_output(GPIOB, 14, enable);

@ -13,6 +13,7 @@
#include "safety/safety_hyundai.h" #include "safety/safety_hyundai.h"
#include "safety/safety_chrysler.h" #include "safety/safety_chrysler.h"
#include "safety/safety_subaru.h" #include "safety/safety_subaru.h"
#include "safety/safety_mazda.h"
#include "safety/safety_elm327.h" #include "safety/safety_elm327.h"
const safety_hooks *current_hooks = &nooutput_hooks; const safety_hooks *current_hooks = &nooutput_hooks;
@ -55,6 +56,8 @@ typedef struct {
#define SAFETY_TESLA 8U #define SAFETY_TESLA 8U
#define SAFETY_CHRYSLER 9U #define SAFETY_CHRYSLER 9U
#define SAFETY_SUBARU 10U #define SAFETY_SUBARU 10U
#define SAFETY_GM_PASSIVE 11U
#define SAFETY_MAZDA 12U
#define SAFETY_GM_ASCM 0x1334U #define SAFETY_GM_ASCM 0x1334U
#define SAFETY_TOYOTA_IPAS 0x1335U #define SAFETY_TOYOTA_IPAS 0x1335U
#define SAFETY_ALLOUTPUT 0x1337U #define SAFETY_ALLOUTPUT 0x1337U
@ -71,7 +74,9 @@ const safety_hook_config safety_hook_registry[] = {
{SAFETY_HYUNDAI, &hyundai_hooks}, {SAFETY_HYUNDAI, &hyundai_hooks},
{SAFETY_CHRYSLER, &chrysler_hooks}, {SAFETY_CHRYSLER, &chrysler_hooks},
{SAFETY_SUBARU, &subaru_hooks}, {SAFETY_SUBARU, &subaru_hooks},
{SAFETY_MAZDA, &mazda_hooks},
{SAFETY_TOYOTA_IPAS, &toyota_ipas_hooks}, {SAFETY_TOYOTA_IPAS, &toyota_ipas_hooks},
{SAFETY_GM_PASSIVE, &gm_passive_hooks},
{SAFETY_GM_ASCM, &gm_ascm_hooks}, {SAFETY_GM_ASCM, &gm_ascm_hooks},
{SAFETY_TESLA, &tesla_hooks}, {SAFETY_TESLA, &tesla_hooks},
{SAFETY_ALLOUTPUT, &alloutput_hooks}, {SAFETY_ALLOUTPUT, &alloutput_hooks},

@ -231,6 +231,20 @@ static int gm_ign_hook(void) {
return gm_ignition_started; return gm_ignition_started;
} }
// All sending is disallowed.
// The only difference from "no output" model
// is using GM ignition hook.
static void gm_passive_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int bus_number = GET_BUS(to_push);
int addr = GET_ADDR(to_push);
if ((addr == 0x1F1) && (bus_number == 0)) {
bool ign = (GET_BYTE(to_push, 0) & 0x20) != 0;
gm_ignition_started = ign;
}
}
const safety_hooks gm_hooks = { const safety_hooks gm_hooks = {
.init = gm_init, .init = gm_init,
.rx = gm_rx_hook, .rx = gm_rx_hook,
@ -239,3 +253,12 @@ const safety_hooks gm_hooks = {
.ignition = gm_ign_hook, .ignition = gm_ign_hook,
.fwd = default_fwd_hook, .fwd = default_fwd_hook,
}; };
const safety_hooks gm_passive_hooks = {
.init = gm_init,
.rx = gm_passive_rx_hook,
.tx = nooutput_tx_hook,
.tx_lin = nooutput_tx_lin_hook,
.ignition = gm_ign_hook,
.fwd = default_fwd_hook,
};

@ -8,16 +8,19 @@
// brake > 0mph // brake > 0mph
const int HONDA_GAS_INTERCEPTOR_THRESHOLD = 328; // ratio between offset and gain from dbc file const int HONDA_GAS_INTERCEPTOR_THRESHOLD = 328; // ratio between offset and gain from dbc file
int honda_brake_prev = 0; int honda_brake = 0;
int honda_gas_prev = 0; int honda_gas_prev = 0;
bool honda_brake_pressed_prev = false;
bool honda_moving = false; bool honda_moving = false;
bool honda_bosch_hardware = false; bool honda_bosch_hardware = false;
bool honda_alt_brake_msg = false; bool honda_alt_brake_msg = false;
bool honda_fwd_brake = false;
static void honda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { static void honda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int addr = GET_ADDR(to_push); int addr = GET_ADDR(to_push);
int len = GET_LEN(to_push); int len = GET_LEN(to_push);
int bus = GET_BUS(to_push);
// sample speed // sample speed
if (addr == 0x158) { if (addr == 0x158) {
@ -51,11 +54,11 @@ static void honda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
// exit controls on rising edge of brake press or on brake press when speed > 0 // exit controls on rising edge of brake press or on brake press when speed > 0
bool is_user_brake_msg = honda_alt_brake_msg ? ((addr) == 0x1BE) : ((addr) == 0x17C); bool is_user_brake_msg = honda_alt_brake_msg ? ((addr) == 0x1BE) : ((addr) == 0x17C);
if (is_user_brake_msg) { if (is_user_brake_msg) {
int brake = honda_alt_brake_msg ? (GET_BYTE((to_push), 0) & 0x10) : (GET_BYTE((to_push), 6) & 0x20); bool brake_pressed = honda_alt_brake_msg ? (GET_BYTE((to_push), 0) & 0x10) : (GET_BYTE((to_push), 6) & 0x20);
if (brake && (!(honda_brake_prev) || honda_moving)) { if (brake_pressed && (!(honda_brake_pressed_prev) || honda_moving)) {
controls_allowed = 0; controls_allowed = 0;
} }
honda_brake_prev = brake; honda_brake_pressed_prev = brake_pressed;
} }
// exit controls on rising edge of gas press if interceptor (0x201 w/ len = 6) // exit controls on rising edge of gas press if interceptor (0x201 w/ len = 6)
@ -81,6 +84,20 @@ static void honda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
honda_gas_prev = gas; honda_gas_prev = gas;
} }
} }
if ((bus == 2) && (addr == 0x1FA)) {
bool honda_stock_aeb = GET_BYTE(to_push, 3) & 0x20;
int honda_stock_brake = (GET_BYTE(to_push, 0) << 2) + ((GET_BYTE(to_push, 1) >> 6) & 0x3);
// Forward AEB when stock braking is higher than openpilot braking
// only stop forwarding when AEB event is over
if (!honda_stock_aeb) {
honda_fwd_brake = false;
} else if (honda_stock_brake >= honda_brake) {
honda_fwd_brake = true;
} else {
// Leave honda forward brake as is
}
}
} }
// all commands: gas, brake and steering // all commands: gas, brake and steering
@ -98,18 +115,21 @@ static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
// disallow actuator commands if gas or brake (with vehicle moving) are pressed // disallow actuator commands if gas or brake (with vehicle moving) are pressed
// and the the latching controls_allowed flag is True // and the the latching controls_allowed flag is True
int pedal_pressed = honda_gas_prev || (gas_interceptor_prev > HONDA_GAS_INTERCEPTOR_THRESHOLD) || int pedal_pressed = honda_gas_prev || (gas_interceptor_prev > HONDA_GAS_INTERCEPTOR_THRESHOLD) ||
(honda_brake_prev && honda_moving); (honda_brake_pressed_prev && honda_moving);
bool current_controls_allowed = controls_allowed && !(pedal_pressed); bool current_controls_allowed = controls_allowed && !(pedal_pressed);
// BRAKE: safety check // BRAKE: safety check
if (addr == 0x1FA) { if ((addr == 0x1FA) && (bus == 0)) {
int brake = (GET_BYTE(to_send, 0) << 2) + (GET_BYTE(to_send, 1) & 0x3); honda_brake = (GET_BYTE(to_send, 0) << 2) + ((GET_BYTE(to_send, 1) >> 6) & 0x3);
if (!current_controls_allowed || !long_controls_allowed) { if (!current_controls_allowed || !long_controls_allowed) {
if (brake != 0) { if (honda_brake != 0) {
tx = 0; tx = 0;
} }
} }
if (brake > 255) { if (honda_brake > 255) {
tx = 0;
}
if (honda_fwd_brake) {
tx = 0; tx = 0;
} }
} }
@ -175,9 +195,12 @@ static int honda_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
if (bus_num == 2) { if (bus_num == 2) {
// block stock lkas messages and stock acc messages (if OP is doing ACC) // block stock lkas messages and stock acc messages (if OP is doing ACC)
int addr = GET_ADDR(to_fwd); int addr = GET_ADDR(to_fwd);
int is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D); bool is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D);
int is_acc_msg = (addr == 0x1FA) || (addr == 0x30C) || (addr == 0x39F); bool is_acc_hud_msg = (addr == 0x30C) || (addr == 0x39F);
int block_fwd = is_lkas_msg || (is_acc_msg && long_controls_allowed); bool is_brake_msg = addr == 0x1FA;
bool block_fwd = is_lkas_msg ||
(is_acc_hud_msg && long_controls_allowed) ||
(is_brake_msg && long_controls_allowed && !honda_fwd_brake);
if (!block_fwd) { if (!block_fwd) {
bus_fwd = 0; bus_fwd = 0;
} }

@ -0,0 +1,169 @@
// CAN msgs we care about
#define MAZDA_LKAS 0x243
#define MAZDA_LANEINFO 0x440
#define MAZDA_CRZ_CTRL 0x21c
#define MAZDA_WHEEL_SPEED 0x215
#define MAZDA_STEER_TORQUE 0x240
// CAN bus numbers
#define MAZDA_MAIN 0
#define MAZDA_AUX 1
#define MAZDA_CAM 2
#define MAZDA_MAX_STEER 2048
// max delta torque allowed for real time checks
#define MAZDA_MAX_RT_DELTA 940
// 250ms between real time checks
#define MAZDA_RT_INTERVAL 250000
#define MAZDA_MAX_RATE_UP 10
#define MAZDA_MAX_RATE_DOWN 25
#define MAZDA_DRIVER_TORQUE_ALLOWANCE 15
#define MAZDA_DRIVER_TORQUE_FACTOR 1
int mazda_cruise_engaged_last = 0;
int mazda_rt_torque_last = 0;
int mazda_desired_torque_last = 0;
uint32_t mazda_ts_last = 0;
struct sample_t mazda_torque_driver; // last few driver torques measured
// track msgs coming from OP so that we know what CAM msgs to drop and what to forward
int mazda_op_lkas_detected = 0;
int mazda_op_laneinfo_detected = 0;
int mazda_forward_cam = 0;
int mazda_giraffe_switch_2_on = 0;
void mazda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int bus = GET_BUS(to_push);
int addr = GET_ADDR(to_push);
if ((addr == MAZDA_STEER_TORQUE) && (bus == MAZDA_MAIN)) {
int torque_driver_new = GET_BYTE(to_push, 0) - 127;
// update array of samples
update_sample(&mazda_torque_driver, torque_driver_new);
}
// enter controls on rising edge of ACC, exit controls on ACC off
if ((addr == MAZDA_CRZ_CTRL) && (bus == MAZDA_MAIN)) {
int cruise_engaged = GET_BYTE(to_push, 0) & 8;
if (cruise_engaged != 0) {
if (!mazda_cruise_engaged_last) {
controls_allowed = 1;
}
}
else {
controls_allowed = 0;
}
mazda_cruise_engaged_last = cruise_engaged;
}
// we have msgs on bus MAZDA_CAM
if (bus == MAZDA_CAM) {
// the stock CAM is connected
if (addr == MAZDA_LKAS) {
mazda_forward_cam = 1;
}
// if we see wheel speed msgs on MAZDA_CAM bus then giraffe switch 2 is high
// (hardware passthru)
if (addr == MAZDA_WHEEL_SPEED) {
mazda_giraffe_switch_2_on = 1;
}
}
}
static int mazda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int tx = 1;
int addr = GET_ADDR(to_send);
int bus = GET_BUS(to_send);
// Check if msg is sent on the main BUS
if (bus == MAZDA_MAIN) {
if ((addr == MAZDA_LKAS) && !mazda_op_lkas_detected){
mazda_op_lkas_detected = 1;
}
if ((addr == MAZDA_LANEINFO) && !mazda_op_laneinfo_detected){
mazda_op_laneinfo_detected = 1;
}
// steer cmd checks
if (addr == MAZDA_LKAS) {
int desired_torque = (((GET_BYTE(to_send, 0) & 0x0f) << 8) | GET_BYTE(to_send, 1)) - MAZDA_MAX_STEER;
bool violation = 0;
uint32_t ts = TIM2->CNT;
if (controls_allowed) {
// *** global torque limit check ***
violation |= max_limit_check(desired_torque, MAZDA_MAX_STEER, -MAZDA_MAX_STEER);
// *** torque rate limit check ***
int desired_torque_last = mazda_desired_torque_last;
violation |= driver_limit_check(desired_torque, desired_torque_last, &mazda_torque_driver,
MAZDA_MAX_STEER, MAZDA_MAX_RATE_UP, MAZDA_MAX_RATE_DOWN,
MAZDA_DRIVER_TORQUE_ALLOWANCE, MAZDA_DRIVER_TORQUE_FACTOR);
// used next time
mazda_desired_torque_last = desired_torque;
// *** torque real time rate limit check ***
violation |= rt_rate_limit_check(desired_torque, mazda_rt_torque_last, MAZDA_MAX_RT_DELTA);
// every RT_INTERVAL set the new limits
uint32_t ts_elapsed = get_ts_elapsed(ts, mazda_ts_last);
if (ts_elapsed > ((uint32_t) MAZDA_RT_INTERVAL)) {
mazda_rt_torque_last = desired_torque;
mazda_ts_last = ts;
}
}
// no torque if controls is not allowed
if (!controls_allowed && (desired_torque != 0)) {
violation = 1;
}
// reset to 0 if either controls is not allowed or there's a violation
if (violation || !controls_allowed) {
mazda_desired_torque_last = 0;
mazda_rt_torque_last = 0;
mazda_ts_last = ts;
}
if (violation) {
tx = 0;
}
}
}
return tx;
}
static int mazda_fwd_hook(int bus, CAN_FIFOMailBox_TypeDef *to_fwd) {
int bus_fwd = -1;
if (mazda_forward_cam && !mazda_giraffe_switch_2_on) {
int addr = GET_ADDR(to_fwd);
if (bus == MAZDA_MAIN) {
bus_fwd = MAZDA_CAM;
}
else if (bus == MAZDA_CAM) {
// drop stock CAM_LKAS and CAM_LANEINFI if OP is sending them
if (!((addr == MAZDA_LKAS) && mazda_op_lkas_detected) &&
!((addr == MAZDA_LANEINFO) && mazda_op_laneinfo_detected)) {
bus_fwd = MAZDA_MAIN;
}
}
else {
bus_fwd = -1;
}
}
return bus_fwd;
}
const safety_hooks mazda_hooks = {
.init = nooutput_init,
.rx = mazda_rx_hook,
.tx = mazda_tx_hook,
.tx_lin = nooutput_tx_lin_hook,
.ignition = default_ign_hook,
.fwd = mazda_fwd_hook,
};

@ -14,21 +14,22 @@ int subaru_desired_torque_last = 0;
uint32_t subaru_ts_last = 0; uint32_t subaru_ts_last = 0;
struct sample_t subaru_torque_driver; // last few driver torques measured struct sample_t subaru_torque_driver; // last few driver torques measured
static void subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { static void subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {
int bus = GET_BUS(to_push); int bus = GET_BUS(to_push);
int addr = GET_ADDR(to_push); int addr = GET_ADDR(to_push);
if ((addr == 0x119) && (bus == 0)){ if (((addr == 0x119) || (addr == 0x371)) && (bus == 0)){
int torque_driver_new = ((GET_BYTES_04(to_push) >> 16) & 0x7FF); int bit_shift = (addr == 0x119) ? 16 : 29;
int torque_driver_new = ((GET_BYTES_04(to_push) >> bit_shift) & 0x7FF);
torque_driver_new = to_signed(torque_driver_new, 11); torque_driver_new = to_signed(torque_driver_new, 11);
// update array of samples // update array of samples
update_sample(&subaru_torque_driver, torque_driver_new); update_sample(&subaru_torque_driver, torque_driver_new);
} }
// enter controls on rising edge of ACC, exit controls on ACC off // enter controls on rising edge of ACC, exit controls on ACC off
if ((addr == 0x240) && (bus == 0)) { if (((addr == 0x240) || (addr == 0x144)) && (bus == 0)) {
int cruise_engaged = GET_BYTE(to_push, 5) & 2; int bit_shift = (addr == 0x240) ? 9 : 17;
int cruise_engaged = ((GET_BYTES_48(to_push) >> bit_shift) & 1);
if (cruise_engaged && !subaru_cruise_engaged_last) { if (cruise_engaged && !subaru_cruise_engaged_last) {
controls_allowed = 1; controls_allowed = 1;
} }
@ -44,8 +45,9 @@ static int subaru_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
int addr = GET_ADDR(to_send); int addr = GET_ADDR(to_send);
// steer cmd checks // steer cmd checks
if (addr == 0x122) { if ((addr == 0x122) || (addr == 0x164)) {
int desired_torque = ((GET_BYTES_04(to_send) >> 16) & 0x1FFF); int bit_shift = (addr == 0x122) ? 16 : 8;
int desired_torque = ((GET_BYTES_04(to_send) >> bit_shift) & 0x1FFF);
bool violation = 0; bool violation = 0;
uint32_t ts = TIM2->CNT; uint32_t ts = TIM2->CNT;
desired_torque = to_signed(desired_torque, 13); desired_torque = to_signed(desired_torque, 13);
@ -102,8 +104,8 @@ static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) {
bus_fwd = 2; // Camera CAN bus_fwd = 2; // Camera CAN
} }
if (bus_num == 2) { if (bus_num == 2) {
// 290 is LKAS for Global Platform
// 356 is LKAS for outback 2015 // 356 is LKAS for outback 2015
// 356 is LKAS for Global Platform
// 545 is ES_Distance // 545 is ES_Distance
// 802 is ES_LKAS // 802 is ES_LKAS
int addr = GET_ADDR(to_fwd); int addr = GET_ADDR(to_fwd);

@ -183,7 +183,7 @@ void CAN1_RX0_IRQHandler(void) {
if ((CAN->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) { if ((CAN->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) {
uint8_t dat[8]; uint8_t dat[8];
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
dat[0] = GET_BYTE(&CAN->sFIFOMailBox[0], i); dat[i] = GET_BYTE(&CAN->sFIFOMailBox[0], i);
} }
uint8_t odat[8]; uint8_t odat[8];
uint8_t type = dat[0] & 0xF0; uint8_t type = dat[0] & 0xF0;
@ -307,7 +307,7 @@ void soft_flasher_start(void) {
// green LED on for flashing // green LED on for flashing
current_board->set_led(LED_GREEN, 1); current_board->set_led(LED_GREEN, 1);
__enable_irq(); enable_interrupts();
uint64_t cnt = 0; uint64_t cnt = 0;

@ -135,6 +135,12 @@ class Panda(object):
REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
HW_TYPE_UNKNOWN = '\x00'
HW_TYPE_WHITE_PANDA = '\x01'
HW_TYPE_GREY_PANDA = '\x02'
HW_TYPE_BLACK_PANDA = '\x03'
HW_TYPE_PEDAL = '\x04'
def __init__(self, serial=None, claim=True): def __init__(self, serial=None, claim=True):
self._serial = serial self._serial = serial
self._handle = None self._handle = None
@ -363,11 +369,14 @@ class Panda(object):
def get_type(self): def get_type(self):
return self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40) return self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40)
def is_white(self):
return self.get_type() == Panda.HW_TYPE_WHITE_PANDA
def is_grey(self): def is_grey(self):
return self.get_type() == "\x02" return self.get_type() == Panda.HW_TYPE_GREY_PANDA
def is_black(self): def is_black(self):
return self.get_type() == "\x03" return self.get_type() == Panda.HW_TYPE_BLACK_PANDA
def get_serial(self): def get_serial(self):
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20) dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20)
@ -470,6 +479,7 @@ class Panda(object):
break break
except (usb1.USBErrorIO, usb1.USBErrorOverflow): except (usb1.USBErrorIO, usb1.USBErrorOverflow):
print("CAN: BAD RECV, RETRYING") print("CAN: BAD RECV, RETRYING")
time.sleep(0.1)
return parse_can_buffer(dat) return parse_can_buffer(dat)
def can_clear(self, bus): def can_clear(self, bus):

@ -1,15 +1,13 @@
import os import os
from panda import Panda from panda import Panda
from helpers import panda_color_to_serial, test_white_and_grey from helpers import panda_type_to_serial, test_white_and_grey, test_all_pandas, panda_connect_and_init
@test_white_and_grey @test_all_pandas
@panda_color_to_serial @panda_connect_and_init
def test_recover(serial=None): def test_recover(p):
p = Panda(serial=serial)
assert p.recover(timeout=30) assert p.recover(timeout=30)
@test_white_and_grey @test_all_pandas
@panda_color_to_serial @panda_connect_and_init
def test_flash(serial=None): def test_flash(p):
p = Panda(serial=serial)
p.flash() p.flash()

@ -4,16 +4,11 @@ import sys
import time import time
from panda import Panda from panda import Panda
from nose.tools import assert_equal, assert_less, assert_greater from nose.tools import assert_equal, assert_less, assert_greater
from helpers import time_many_sends, connect_wo_esp, test_white_and_grey, panda_color_to_serial from helpers import SPEED_NORMAL, SPEED_GMLAN, time_many_sends, test_white_and_grey, panda_type_to_serial, test_all_pandas, panda_connect_and_init
SPEED_NORMAL = 500
SPEED_GMLAN = 33.3
@test_white_and_grey
@panda_color_to_serial
def test_can_loopback(serial=None):
p = connect_wo_esp(serial)
@test_all_pandas
@panda_connect_and_init
def test_can_loopback(p):
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
@ -26,9 +21,6 @@ def test_can_loopback(serial=None):
busses = [0,1,2] busses = [0,1,2]
for bus in busses: for bus in busses:
# send heartbeat
p.send_heartbeat()
# set bus 0 speed to 250 # set bus 0 speed to 250
p.set_can_speed_kbps(bus, 250) p.set_can_speed_kbps(bus, 250)
@ -47,17 +39,12 @@ def test_can_loopback(serial=None):
assert 0x1aa == sr[0][0] == lb[0][0] assert 0x1aa == sr[0][0] == lb[0][0]
assert "message" == sr[0][2] == lb[0][2] assert "message" == sr[0][2] == lb[0][2]
@test_white_and_grey @test_all_pandas
@panda_color_to_serial @panda_connect_and_init
def test_safety_nooutput(serial=None): def test_safety_nooutput(p):
p = connect_wo_esp(serial)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_NOOUTPUT) p.set_safety_mode(Panda.SAFETY_NOOUTPUT)
# send heartbeat
p.send_heartbeat()
# enable CAN loopback mode # enable CAN loopback mode
p.set_can_loopback(True) p.set_can_loopback(True)
@ -69,11 +56,9 @@ def test_safety_nooutput(serial=None):
r = p.can_recv() r = p.can_recv()
assert len(r) == 0 assert len(r) == 0
@test_white_and_grey @test_all_pandas
@panda_color_to_serial @panda_connect_and_init
def test_reliability(serial=None): def test_reliability(p):
p = connect_wo_esp(serial)
LOOP_COUNT = 100 LOOP_COUNT = 100
MSG_COUNT = 100 MSG_COUNT = 100
@ -82,17 +67,11 @@ def test_reliability(serial=None):
p.set_can_loopback(True) p.set_can_loopback(True)
p.set_can_speed_kbps(0, 1000) p.set_can_speed_kbps(0, 1000)
# send heartbeat
p.send_heartbeat()
addrs = range(100, 100+MSG_COUNT) addrs = range(100, 100+MSG_COUNT)
ts = [(j, 0, "\xaa"*8, 0) for j in addrs] ts = [(j, 0, "\xaa"*8, 0) for j in addrs]
# 100 loops # 100 loops
for i in range(LOOP_COUNT): for i in range(LOOP_COUNT):
# send heartbeat
p.send_heartbeat()
st = time.time() st = time.time()
p.can_send_many(ts) p.can_send_many(ts)
@ -115,17 +94,12 @@ def test_reliability(serial=None):
sys.stdout.write("P") sys.stdout.write("P")
sys.stdout.flush() sys.stdout.flush()
@test_white_and_grey @test_all_pandas
@panda_color_to_serial @panda_connect_and_init
def test_throughput(serial=None): def test_throughput(p):
p = connect_wo_esp(serial)
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# send heartbeat
p.send_heartbeat()
# enable CAN loopback mode # enable CAN loopback mode
p.set_can_loopback(True) p.set_can_loopback(True)
@ -134,9 +108,6 @@ def test_throughput(serial=None):
p.set_can_speed_kbps(0, speed) p.set_can_speed_kbps(0, speed)
time.sleep(0.05) time.sleep(0.05)
# send heartbeat
p.send_heartbeat()
comp_kbps = time_many_sends(p, 0) comp_kbps = time_many_sends(p, 0)
# bit count from https://en.wikipedia.org/wiki/CAN_bus # bit count from https://en.wikipedia.org/wiki/CAN_bus
@ -147,19 +118,15 @@ def test_throughput(serial=None):
print("loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct)) print("loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct))
@test_white_and_grey @test_white_and_grey
@panda_color_to_serial @panda_type_to_serial
def test_gmlan(serial=None): @panda_connect_and_init
p = connect_wo_esp(serial) def test_gmlan(p):
if p.legacy: if p.legacy:
return return
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# send heartbeat
p.send_heartbeat()
# enable CAN loopback mode # enable CAN loopback mode
p.set_can_loopback(True) p.set_can_loopback(True)
@ -169,9 +136,6 @@ def test_gmlan(serial=None):
# set gmlan on CAN2 # set gmlan on CAN2
for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3, Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3, Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]:
# send heartbeat
p.send_heartbeat()
p.set_gmlan(bus) p.set_gmlan(bus)
comp_kbps_gmlan = time_many_sends(p, 3) comp_kbps_gmlan = time_many_sends(p, 3)
assert_greater(comp_kbps_gmlan, 0.8 * SPEED_GMLAN) assert_greater(comp_kbps_gmlan, 0.8 * SPEED_GMLAN)
@ -185,27 +149,20 @@ def test_gmlan(serial=None):
print("%d: %.2f kbps vs %.2f kbps" % (bus, comp_kbps_gmlan, comp_kbps_normal)) print("%d: %.2f kbps vs %.2f kbps" % (bus, comp_kbps_gmlan, comp_kbps_normal))
@test_white_and_grey @test_white_and_grey
@panda_color_to_serial @panda_type_to_serial
def test_gmlan_bad_toggle(serial=None): @panda_connect_and_init
p = connect_wo_esp(serial) def test_gmlan_bad_toggle(p):
if p.legacy: if p.legacy:
return return
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# send heartbeat
p.send_heartbeat()
# enable CAN loopback mode # enable CAN loopback mode
p.set_can_loopback(True) p.set_can_loopback(True)
# GMLAN_CAN2 # GMLAN_CAN2
for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]:
# send heartbeat
p.send_heartbeat()
p.set_gmlan(bus) p.set_gmlan(bus)
comp_kbps_gmlan = time_many_sends(p, 3) comp_kbps_gmlan = time_many_sends(p, 3)
assert_greater(comp_kbps_gmlan, 0.6 * SPEED_GMLAN) assert_greater(comp_kbps_gmlan, 0.6 * SPEED_GMLAN)
@ -213,9 +170,6 @@ def test_gmlan_bad_toggle(serial=None):
# normal # normal
for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]:
# send heartbeat
p.send_heartbeat()
p.set_gmlan(None) p.set_gmlan(None)
comp_kbps_normal = time_many_sends(p, bus) comp_kbps_normal = time_many_sends(p, bus)
assert_greater(comp_kbps_normal, 0.6 * SPEED_NORMAL) assert_greater(comp_kbps_normal, 0.6 * SPEED_NORMAL)
@ -223,10 +177,9 @@ def test_gmlan_bad_toggle(serial=None):
# this will fail if you have hardware serial connected # this will fail if you have hardware serial connected
@test_white_and_grey @test_all_pandas
@panda_color_to_serial @panda_connect_and_init
def test_serial_debug(serial=None): def test_serial_debug(p):
p = connect_wo_esp(serial)
junk = p.serial_read(Panda.SERIAL_DEBUG) junk = p.serial_read(Panda.SERIAL_DEBUG)
p.call_control_api(0xc0) p.call_control_api(0xc0)
assert(p.serial_read(Panda.SERIAL_DEBUG).startswith("can ")) assert(p.serial_read(Panda.SERIAL_DEBUG).startswith("can "))

@ -2,46 +2,44 @@ from __future__ import print_function
import os import os
import time import time
from panda import Panda from panda import Panda
from helpers import connect_wifi, test_white, test_white_and_grey, panda_color_to_serial from helpers import connect_wifi, test_white, test_all_pandas, panda_type_to_serial, panda_connect_and_init
import requests import requests
@test_white_and_grey @test_all_pandas
@panda_color_to_serial @panda_connect_and_init
def test_get_serial(serial=None): def test_get_serial(p):
p = Panda(serial)
print(p.get_serial()) print(p.get_serial())
@test_white_and_grey @test_all_pandas
@panda_color_to_serial @panda_connect_and_init
def test_get_serial_in_flash_mode(serial=None): def test_get_serial_in_flash_mode(p):
p = Panda(serial)
p.reset(enter_bootstub=True) p.reset(enter_bootstub=True)
assert(p.bootstub) assert(p.bootstub)
print(p.get_serial()) print(p.get_serial())
p.reset() p.reset()
@test_white @test_white
@panda_color_to_serial @panda_type_to_serial
def test_connect_wifi(serial=None): def test_connect_wifi(serials=None):
connect_wifi(serial) connect_wifi(serials[0])
@test_white @test_white
@panda_color_to_serial @panda_type_to_serial
def test_flash_wifi(serial=None): def test_flash_wifi(serials=None):
connect_wifi(serial) connect_wifi(serials[0])
assert Panda.flash_ota_wifi(release=False), "OTA Wifi Flash Failed" assert Panda.flash_ota_wifi(release=False), "OTA Wifi Flash Failed"
connect_wifi(serial) connect_wifi(serials[0])
@test_white @test_white
@panda_color_to_serial @panda_type_to_serial
def test_wifi_flash_st(serial=None): def test_wifi_flash_st(serials=None):
connect_wifi(serial) connect_wifi(serials[0])
assert Panda.flash_ota_st(), "OTA ST Flash Failed" assert Panda.flash_ota_st(), "OTA ST Flash Failed"
connected = False connected = False
st = time.time() st = time.time()
while not connected and (time.time() - st) < 20: while not connected and (time.time() - st) < 20:
try: try:
p = Panda(serial=serial) p = Panda(serial=serials[0])
p.get_serial() p.get_serial()
connected = True connected = True
except: except:
@ -51,9 +49,9 @@ def test_wifi_flash_st(serial=None):
assert False, "Panda failed to connect on USB after flashing" assert False, "Panda failed to connect on USB after flashing"
@test_white @test_white
@panda_color_to_serial @panda_type_to_serial
def test_webpage_fetch(serial=None): def test_webpage_fetch(serials=None):
connect_wifi(serial) connect_wifi(serials[0])
r = requests.get("http://192.168.0.10/") r = requests.get("http://192.168.0.10/")
print(r.text) print(r.text)

@ -1,38 +1,32 @@
from __future__ import print_function from __future__ import print_function
import time import time
from panda import Panda from panda import Panda
from helpers import time_many_sends, connect_wifi, test_white, panda_color_to_serial from helpers import time_many_sends, connect_wifi, test_white, panda_type_to_serial
from nose.tools import timed, assert_equal, assert_less, assert_greater from nose.tools import timed, assert_equal, assert_less, assert_greater
@test_white @test_white
@panda_color_to_serial @panda_type_to_serial
def test_get_serial_wifi(serial=None): def test_get_serial_wifi(serials=None):
connect_wifi(serial) connect_wifi(serials[0])
p = Panda("WIFI") p = Panda("WIFI")
print(p.get_serial()) print(p.get_serial())
@test_white @test_white
@panda_color_to_serial @panda_type_to_serial
def test_throughput(serial=None): def test_throughput(serials=None):
connect_wifi(serial) connect_wifi(serials[0])
p = Panda(serial) p = Panda(serials[0])
# enable output mode # enable output mode
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# send heartbeat
p.send_heartbeat()
# enable CAN loopback mode # enable CAN loopback mode
p.set_can_loopback(True) p.set_can_loopback(True)
p = Panda("WIFI") p = Panda("WIFI")
for speed in [100,250,500,750,1000]: for speed in [100,250,500,750,1000]:
# send heartbeat
p.send_heartbeat()
# set bus 0 speed to speed # set bus 0 speed to speed
p.set_can_speed_kbps(0, speed) p.set_can_speed_kbps(0, speed)
time.sleep(0.1) time.sleep(0.1)
@ -47,23 +41,17 @@ def test_throughput(serial=None):
print("WIFI loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct)) print("WIFI loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct))
@test_white @test_white
@panda_color_to_serial @panda_type_to_serial
def test_recv_only(serial=None): def test_recv_only(serials=None):
connect_wifi(serial) connect_wifi(serials[0])
p = Panda(serial) p = Panda(serials[0])
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# send heartbeat
p.send_heartbeat()
p.set_can_loopback(True) p.set_can_loopback(True)
pwifi = Panda("WIFI") pwifi = Panda("WIFI")
# TODO: msg_count=1000 drops packets, is this fixable? # TODO: msg_count=1000 drops packets, is this fixable?
for msg_count in [10,100,200]: for msg_count in [10,100,200]:
# send heartbeat
p.send_heartbeat()
speed = 500 speed = 500
p.set_can_speed_kbps(0, speed) p.set_can_speed_kbps(0, speed)
comp_kbps = time_many_sends(p, 0, pwifi, msg_count) comp_kbps = time_many_sends(p, 0, pwifi, msg_count)

@ -1,16 +1,16 @@
from __future__ import print_function from __future__ import print_function
import sys import sys
import time import time
from helpers import time_many_sends, connect_wifi, test_white, panda_color_to_serial from helpers import time_many_sends, connect_wifi, test_white, panda_type_to_serial
from panda import Panda, PandaWifiStreaming from panda import Panda, PandaWifiStreaming
from nose.tools import timed, assert_equal, assert_less, assert_greater from nose.tools import timed, assert_equal, assert_less, assert_greater
@test_white @test_white
@panda_color_to_serial @panda_type_to_serial
def test_udp_doesnt_drop(serial=None): def test_udp_doesnt_drop(serials=None):
connect_wifi(serial) connect_wifi(serials[0])
p = Panda(serial) p = Panda(serials[0])
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p.set_can_loopback(True) p.set_can_loopback(True)

@ -1,21 +1,18 @@
from __future__ import print_function from __future__ import print_function
import os
import time import time
import random
from panda import Panda from panda import Panda
from nose.tools import assert_equal, assert_less, assert_greater from nose.tools import assert_equal, assert_less, assert_greater
from helpers import time_many_sends, test_two_panda, panda_color_to_serial from helpers import time_many_sends, test_two_panda, test_two_black_panda, panda_type_to_serial, clear_can_buffers, panda_connect_and_init
@test_two_panda @test_two_panda
@panda_color_to_serial @panda_type_to_serial
def test_send_recv(serial_sender=None, serial_reciever=None): @panda_connect_and_init
p_send = Panda(serial_sender) def test_send_recv(p_send, p_recv):
p_recv = Panda(serial_reciever)
p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_send.set_can_loopback(False) p_send.set_can_loopback(False)
# send heartbeat
p_send.send_heartbeat()
p_recv.set_can_loopback(False) p_recv.set_can_loopback(False)
assert not p_send.legacy assert not p_send.legacy
@ -30,9 +27,6 @@ def test_send_recv(serial_sender=None, serial_reciever=None):
for bus in busses: for bus in busses:
for speed in [100, 250, 500, 750, 1000]: for speed in [100, 250, 500, 750, 1000]:
# send heartbeat
p_send.send_heartbeat()
p_send.set_can_speed_kbps(bus, speed) p_send.set_can_speed_kbps(bus, speed)
p_recv.set_can_speed_kbps(bus, speed) p_recv.set_can_speed_kbps(bus, speed)
time.sleep(0.05) time.sleep(0.05)
@ -46,18 +40,12 @@ def test_send_recv(serial_sender=None, serial_reciever=None):
print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct)) print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct))
@test_two_panda @test_two_panda
@panda_color_to_serial @panda_type_to_serial
def test_latency(serial_sender=None, serial_reciever=None): @panda_connect_and_init
p_send = Panda(serial_sender) def test_latency(p_send, p_recv):
p_recv = Panda(serial_reciever)
# send heartbeat
p_send.send_heartbeat()
p_recv.send_heartbeat()
p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
p_send.set_can_loopback(False) p_send.set_can_loopback(False)
p_recv.set_can_loopback(False) p_recv.set_can_loopback(False)
assert not p_send.legacy assert not p_send.legacy
@ -72,29 +60,17 @@ def test_latency(serial_sender=None, serial_reciever=None):
p_recv.can_recv() p_recv.can_recv()
p_send.can_recv() p_send.can_recv()
# send heartbeat
p_send.send_heartbeat()
p_recv.send_heartbeat()
busses = [0,1,2] busses = [0,1,2]
for bus in busses: for bus in busses:
for speed in [100, 250, 500, 750, 1000]: for speed in [100, 250, 500, 750, 1000]:
# send heartbeat
p_send.send_heartbeat()
p_recv.send_heartbeat()
p_send.set_can_speed_kbps(bus, speed) p_send.set_can_speed_kbps(bus, speed)
p_recv.set_can_speed_kbps(bus, speed) p_recv.set_can_speed_kbps(bus, speed)
time.sleep(0.1) time.sleep(0.1)
#clear can buffers #clear can buffers
r = [1] clear_can_buffers(p_send)
while len(r) > 0: clear_can_buffers(p_recv)
r = p_send.can_recv()
r = [1]
while len(r) > 0:
r = p_recv.can_recv()
time.sleep(0.05)
latencies = [] latencies = []
comp_kbps_list = [] comp_kbps_list = []
@ -137,3 +113,83 @@ def test_latency(serial_sender=None, serial_reciever=None):
print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\ print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\
.format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct)) .format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct))
@test_two_black_panda
@panda_type_to_serial
@panda_connect_and_init
def test_black_loopback(panda0, panda1):
# disable safety modes
panda0.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
panda1.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# disable loopback
panda0.set_can_loopback(False)
panda1.set_can_loopback(False)
# clear stuff
panda0.can_send_many([(0x1ba, 0, "testmsg", 0)]*10)
time.sleep(0.05)
panda0.can_recv()
panda1.can_recv()
# test array (send bus, sender obd, reciever obd, expected busses)
test_array = [
(0, False, False, [0]),
(1, False, False, [1]),
(2, False, False, [2]),
(0, False, True, [0, 1]),
(1, False, True, []),
(2, False, True, [2]),
(0, True, False, [0]),
(1, True, False, [0]),
(2, True, False, [2]),
(0, True, True, [0, 1]),
(1, True, True, [0, 1]),
(2, True, True, [2])
]
# test functions
def get_test_string():
return b"test"+os.urandom(10)
def _test_buses(send_panda, recv_panda, _test_array):
for send_bus, send_obd, recv_obd, recv_buses in _test_array:
print("\nSend bus:", send_bus, " Send OBD:", send_obd, " Recv OBD:", recv_obd)
# set OBD on pandas
send_panda.set_gmlan(True if send_obd else None)
recv_panda.set_gmlan(True if recv_obd else None)
# clear buffers
clear_can_buffers(send_panda)
clear_can_buffers(recv_panda)
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
send_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
cans_echo = send_panda.can_recv()
cans_loop = recv_panda.can_recv()
loop_buses = []
for loop in cans_loop:
print(" Loop on bus", str(loop[3]))
loop_buses.append(loop[3])
if len(cans_loop) == 0:
print(" No loop")
# test loop buses
recv_buses.sort()
loop_buses.sort()
assert recv_buses == loop_buses
print(" TEST PASSED")
print("\n")
# test both orientations
print("***************** TESTING (0 --> 1) *****************")
_test_buses(panda0, panda1, test_array)
print("***************** TESTING (1 --> 0) *****************")
_test_buses(panda1, panda0, test_array)

@ -4,43 +4,41 @@ import time
import random import random
import subprocess import subprocess
import requests import requests
import thread
from functools import wraps from functools import wraps
from panda import Panda from panda import Panda
from nose.tools import timed, assert_equal, assert_less, assert_greater from nose.tools import timed, assert_equal, assert_less, assert_greater
from parameterized import parameterized, param from parameterized import parameterized, param
test_white_and_grey = parameterized([param(panda_color="White"), SPEED_NORMAL = 500
param(panda_color="Grey")]) SPEED_GMLAN = 33.3
test_white = parameterized([param(panda_color="White")])
test_grey = parameterized([param(panda_color="Grey")])
test_two_panda = parameterized([param(panda_color=["Grey", "White"]),
param(panda_color=["White", "Grey"])])
_serials = {}
def get_panda_serial(is_grey=None):
global _serials
if is_grey not in _serials:
for serial in Panda.list():
p = Panda(serial=serial)
if is_grey is None or p.is_grey() == is_grey:
_serials[is_grey] = serial
return serial
raise IOError("Panda not found. is_grey: {}".format(is_grey))
else:
return _serials[is_grey]
def connect_wo_esp(serial=None):
# connect to the panda
p = Panda(serial=serial)
# power down the ESP
p.set_esp_power(False)
# clear old junk test_all_types = parameterized([
while len(p.can_recv()) > 0: param(panda_type=Panda.HW_TYPE_WHITE_PANDA),
pass param(panda_type=Panda.HW_TYPE_GREY_PANDA),
param(panda_type=Panda.HW_TYPE_BLACK_PANDA)
return p ])
test_all_pandas = parameterized(
Panda.list()
)
test_white_and_grey = parameterized([
param(panda_type=Panda.HW_TYPE_WHITE_PANDA),
param(panda_type=Panda.HW_TYPE_GREY_PANDA)
])
test_white = parameterized([
param(panda_type=Panda.HW_TYPE_WHITE_PANDA)
])
test_grey = parameterized([
param(panda_type=Panda.HW_TYPE_GREY_PANDA)
])
test_two_panda = parameterized([
param(panda_type=[Panda.HW_TYPE_GREY_PANDA, Panda.HW_TYPE_WHITE_PANDA]),
param(panda_type=[Panda.HW_TYPE_WHITE_PANDA, Panda.HW_TYPE_GREY_PANDA]),
param(panda_type=[Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_BLACK_PANDA])
])
test_two_black_panda = parameterized([
param(panda_type=[Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_BLACK_PANDA])
])
def connect_wifi(serial=None): def connect_wifi(serial=None):
p = Panda(serial=serial) p = Panda(serial=serial)
@ -170,23 +168,93 @@ def time_many_sends(p, bus, precv=None, msg_count=100, msg_id=None, two_pandas=F
return comp_kbps return comp_kbps
_panda_serials = None
def panda_type_to_serial(fn):
@wraps(fn)
def wrapper(panda_type=None, **kwargs):
# Change panda_types to a list
if panda_type is not None:
if not isinstance(panda_type, list):
panda_type = [panda_type]
# If not done already, get panda serials and their type
global _panda_serials
if _panda_serials == None:
_panda_serials = []
for serial in Panda.list():
p = Panda(serial=serial)
_panda_serials.append((serial, p.get_type()))
p.close()
# Find a panda with the correct types and add the corresponding serial
serials = []
for p_type in panda_type:
found = False
for serial, pt in _panda_serials:
# Never take the same panda twice
if (pt == p_type) and (serial not in serials):
serials.append(serial)
found = True
break
if not found:
raise IOError("No unused panda found for type: {}".format(p_type))
return fn(serials, **kwargs)
return wrapper
def heartbeat_thread(p):
while True:
try:
p.send_heartbeat()
time.sleep(1)
except:
break
def panda_color_to_serial(fn): def panda_connect_and_init(fn):
@wraps(fn) @wraps(fn)
def wrapper(panda_color=None, **kwargs): def wrapper(panda_serials=None, **kwargs):
pandas_is_grey = [] # Change panda_serials to a list
if panda_color is not None: if panda_serials is not None:
if not isinstance(panda_color, list): if not isinstance(panda_serials, list):
panda_color = [panda_color] panda_serials = [panda_serials]
panda_color = [s.lower() for s in panda_color]
for p in panda_color: # Connect to pandas
if p is None: pandas = []
pandas_is_grey.append(None) for panda_serial in panda_serials:
elif p in ["grey", "gray"]: pandas.append(Panda(serial=panda_serial))
pandas_is_grey.append(True)
elif p in ["white"]: # Initialize pandas
pandas_is_grey.append(False) for panda in pandas:
else: panda.set_can_loopback(False)
raise ValueError("Invalid Panda Color {}".format(p)) panda.set_gmlan(None)
return fn(*[get_panda_serial(is_grey) for is_grey in pandas_is_grey], **kwargs) panda.set_esp_power(False)
for bus, speed in [(0, SPEED_NORMAL), (1, SPEED_NORMAL), (2, SPEED_NORMAL), (3, SPEED_GMLAN)]:
panda.set_can_speed_kbps(bus, speed)
clear_can_buffers(panda)
thread.start_new_thread(heartbeat_thread, (panda,))
# Run test function
ret = fn(*pandas, **kwargs)
# Close all connections
for panda in pandas:
panda.close()
# Return test function result
return ret
return wrapper return wrapper
def clear_can_buffers(panda):
# clear tx buffers
for i in range(4):
panda.can_clear(i)
# clear rx buffers
panda.can_clear(0xFFFF)
r = [1]
st = time.time()
while len(r) > 0:
r = panda.can_recv()
time.sleep(0.05)
if (time.time() - st) > 10:
print("Unable to clear can buffers for panda ", panda.get_serial())
assert False

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# Loopback test between black panda (+ harness and power) and white/grey panda # Loopback test between two black pandas (+ harness and power)
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test. # Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test.
# To be sure, the test should be run with both harness orientations # To be sure, the test should be run with both harness orientations
@ -33,81 +33,70 @@ def run_test(sleep_duration):
pandas[0] = Panda(pandas[0]) pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1]) pandas[1] = Panda(pandas[1])
# find out which one is black # find out the hardware types
type0 = pandas[0].get_type() type0 = pandas[0].get_type()
type1 = pandas[1].get_type() type1 = pandas[1].get_type()
black_panda = None
other_panda = None
if type0 == "\x03" and type1 != "\x03": if type0 != "\x03" or type1 != "\x03":
black_panda = pandas[0] print("Connect two black pandas to run this test!")
other_panda = pandas[1]
elif type0 != "\x03" and type1 == "\x03":
black_panda = pandas[1]
other_panda = pandas[0]
else:
print("Connect white/grey and black panda to run this test!")
assert False assert False
# disable safety modes for panda in pandas:
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) # disable safety modes
other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test health packet # test health packet
print("black panda health", black_panda.health()) print("panda health", panda.health())
print("other panda health", other_panda.health())
# setup test array (send bus, sender obd, reciever obd, expected busses)
# test black -> other test_array = [
test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration) (0, False, False, [0]),
test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration) (1, False, False, [1]),
(2, False, False, [2]),
(0, False, True, [0, 1]),
(1, False, True, []),
(2, False, True, [2]),
(0, True, False, [0]),
(1, True, False, [0]),
(2, True, False, [2]),
(0, True, True, [0, 1]),
(1, True, True, [0, 1]),
(2, True, True, [2])
]
# test both orientations
print("***************** TESTING (0 --> 1) *****************")
test_buses(pandas[0], pandas[1], test_array, sleep_duration)
print("***************** TESTING (1 --> 0) *****************")
test_buses(pandas[1], pandas[0], test_array, sleep_duration)
def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): def test_buses(send_panda, recv_panda, test_array, sleep_duration):
if direction: for send_bus, send_obd, recv_obd, recv_buses in test_array:
print("***************** TESTING (BLACK --> OTHER) *****************") send_panda.send_heartbeat()
else: recv_panda.send_heartbeat()
print("***************** TESTING (OTHER --> BLACK) *****************") print("\nSend bus:", send_bus, " Send OBD:", send_obd, " Recv OBD:", recv_obd)
for send_bus, obd, recv_buses in test_array:
black_panda.send_heartbeat()
other_panda.send_heartbeat()
print("\ntest can: ", send_bus, " OBD: ", obd)
# set OBD on black panda # set OBD on pandas
black_panda.set_gmlan(True if obd else None) send_panda.set_gmlan(True if send_obd else None)
recv_panda.set_gmlan(True if recv_obd else None)
# clear and flush # clear and flush
if direction: send_panda.can_clear(send_bus)
black_panda.can_clear(send_bus)
else:
other_panda.can_clear(send_bus)
for recv_bus in recv_buses: for recv_bus in recv_buses:
if direction: recv_panda.can_clear(recv_bus)
other_panda.can_clear(recv_bus) send_panda.can_recv()
else: recv_panda.can_recv()
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters # send the characters
at = random.randint(1, 2000) at = random.randint(1, 2000)
st = get_test_string()[0:8] st = get_test_string()[0:8]
if direction: send_panda.can_send(at, st, send_bus)
black_panda.can_send(at, st, send_bus)
else:
other_panda.can_send(at, st, send_bus)
time.sleep(0.1) time.sleep(0.1)
# check for receive # check for receive
if direction: cans_echo = send_panda.can_recv()
cans_echo = black_panda.can_recv() cans_loop = recv_panda.can_recv()
cans_loop = other_panda.can_recv()
else:
cans_echo = other_panda.can_recv()
cans_loop = black_panda.can_recv()
loop_buses = [] loop_buses = []
for loop in cans_loop: for loop in cans_loop:

@ -0,0 +1,169 @@
#!/usr/bin/env python
# Loopback test between black panda (+ harness and power) and white/grey panda
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test.
# To be sure, the test should be run with both harness orientations
from __future__ import print_function
import os
import sys
import time
import random
import argparse
from hexdump import hexdump
from itertools import permutations
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
counter = 0
nonzero_bus_errors = 0
zero_bus_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter, nonzero_bus_errors, zero_bus_errors, content_errors
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
print("Connect white/grey and black panda to run this test!")
assert False
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
# find out which one is black
type0 = pandas[0].get_type()
type1 = pandas[1].get_type()
black_panda = None
other_panda = None
if type0 == "\x03" and type1 != "\x03":
black_panda = pandas[0]
other_panda = pandas[1]
elif type0 != "\x03" and type1 == "\x03":
black_panda = pandas[1]
other_panda = pandas[0]
else:
print("Connect white/grey and black panda to run this test!")
assert False
# disable safety modes
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
while True:
test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration)
test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration)
counter += 1
print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, "Content errors:", content_errors)
# Toggle relay
black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT)
time.sleep(1)
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
time.sleep(1)
def test_buses(black_panda, other_panda, direction, test_array, sleep_duration):
global nonzero_bus_errors, zero_bus_errors, content_errors
if direction:
print("***************** TESTING (BLACK --> OTHER) *****************")
else:
print("***************** TESTING (OTHER --> BLACK) *****************")
for send_bus, obd, recv_buses in test_array:
black_panda.send_heartbeat()
other_panda.send_heartbeat()
print("\ntest can: ", send_bus, " OBD: ", obd)
# set OBD on black panda
black_panda.set_gmlan(True if obd else None)
# clear and flush
if direction:
black_panda.can_clear(send_bus)
else:
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
if direction:
other_panda.can_clear(recv_bus)
else:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
if direction:
black_panda.can_send(at, st, send_bus)
else:
other_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
if direction:
cans_echo = black_panda.can_recv()
cans_loop = other_panda.can_recv()
else:
cans_echo = other_panda.can_recv()
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[2] != st):
content_errors += 1
print(" Loop on bus", str(loop[3]))
loop_buses.append(loop[3])
if len(cans_loop) == 0:
print(" No loop")
if not os.getenv("NOASSERT"):
assert False
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
if len(loop_buses) == 0:
zero_bus_errors += 1
else:
nonzero_bus_errors += 1
if not os.getenv("NOASSERT"):
assert False
else:
print(" TEST PASSED")
time.sleep(sleep_duration)
print("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)

@ -0,0 +1,175 @@
#!/usr/bin/env python
# Loopback test between black panda (+ harness and power) and white/grey panda
# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test.
# To be sure, the test should be run with both harness orientations
from __future__ import print_function
import os
import sys
import time
import random
import argparse
from hexdump import hexdump
from itertools import permutations
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
counter = 0
nonzero_bus_errors = 0
zero_bus_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter, nonzero_bus_errors, zero_bus_errors, content_errors
pandas = Panda.list()
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
print("Connect white/grey and black panda to run this test!")
assert False
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
# find out which one is black
type0 = pandas[0].get_type()
type1 = pandas[1].get_type()
black_panda = None
other_panda = None
if type0 == "\x03" and type1 != "\x03":
black_panda = pandas[0]
other_panda = pandas[1]
elif type0 != "\x03" and type1 == "\x03":
black_panda = pandas[1]
other_panda = pandas[0]
else:
print("Connect white/grey and black panda to run this test!")
assert False
# disable safety modes
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
start_time = time.time()
temp_start_time = start_time
while True:
test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration)
test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration)
counter += 1
runtime = time.time() - start_time
print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, "Content errors:", content_errors, "Runtime: ", runtime)
if (time.time() - temp_start_time) > 3600*6:
# Toggle relay
black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT)
time.sleep(1)
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
time.sleep(1)
temp_start_time = time.time()
def test_buses(black_panda, other_panda, direction, test_array, sleep_duration):
global nonzero_bus_errors, zero_bus_errors, content_errors
if direction:
print("***************** TESTING (BLACK --> OTHER) *****************")
else:
print("***************** TESTING (OTHER --> BLACK) *****************")
for send_bus, obd, recv_buses in test_array:
black_panda.send_heartbeat()
other_panda.send_heartbeat()
print("\ntest can: ", send_bus, " OBD: ", obd)
# set OBD on black panda
black_panda.set_gmlan(True if obd else None)
# clear and flush
if direction:
black_panda.can_clear(send_bus)
else:
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
if direction:
other_panda.can_clear(recv_bus)
else:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
if direction:
black_panda.can_send(at, st, send_bus)
else:
other_panda.can_send(at, st, send_bus)
time.sleep(0.1)
# check for receive
if direction:
cans_echo = black_panda.can_recv()
cans_loop = other_panda.can_recv()
else:
cans_echo = other_panda.can_recv()
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[2] != st):
content_errors += 1
print(" Loop on bus", str(loop[3]))
loop_buses.append(loop[3])
if len(cans_loop) == 0:
print(" No loop")
if not os.getenv("NOASSERT"):
assert False
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
if len(loop_buses) == 0:
zero_bus_errors += 1
else:
nonzero_bus_errors += 1
if not os.getenv("NOASSERT"):
assert False
else:
print(" TEST PASSED")
time.sleep(sleep_duration)
print("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)

@ -0,0 +1,145 @@
#!/usr/bin/env python
# Relay test with loopback between black panda (+ harness and power) and white/grey panda
# Tests the relay switching multiple times / second by looking at the buses on which loop occurs.
from __future__ import print_function
import os
import sys
import time
import random
import argparse
from hexdump import hexdump
from itertools import permutations
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
from panda import Panda
def get_test_string():
return b"test"+os.urandom(10)
counter = 0
open_errors = 0
closed_errors = 0
content_errors = 0
def run_test(sleep_duration):
global counter, open_errors, closed_errors, content_errors
pandas = Panda.list()
#pandas = ["540046000c51363338383037", "07801b800f51363038363036"]
print(pandas)
# make sure two pandas are connected
if len(pandas) != 2:
print("Connect white/grey and black panda to run this test!")
assert False
# connect
pandas[0] = Panda(pandas[0])
pandas[1] = Panda(pandas[1])
# find out which one is black
type0 = pandas[0].get_type()
type1 = pandas[1].get_type()
black_panda = None
other_panda = None
if type0 == "\x03" and type1 != "\x03":
black_panda = pandas[0]
other_panda = pandas[1]
elif type0 != "\x03" and type1 == "\x03":
black_panda = pandas[1]
other_panda = pandas[0]
else:
print("Connect white/grey and black panda to run this test!")
assert False
# disable safety modes
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
# test health packet
print("black panda health", black_panda.health())
print("other panda health", other_panda.health())
# test black -> other
while True:
# Switch on relay
black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
time.sleep(0.05)
if not test_buses(black_panda, other_panda, (0, False, [0])):
open_errors += 1
print("Open error")
assert False
# Switch off relay
black_panda.set_safety_mode(Panda.SAFETY_NOOUTPUT)
time.sleep(0.05)
if not test_buses(black_panda, other_panda, (0, False, [0, 2])):
closed_errors += 1
print("Close error")
assert False
counter += 1
print("Number of cycles:", counter, "Open errors:", open_errors, "Closed errors:", closed_errors, "Content errors:", content_errors)
def test_buses(black_panda, other_panda, test_obj):
global content_errors
send_bus, obd, recv_buses = test_obj
black_panda.send_heartbeat()
other_panda.send_heartbeat()
# Set OBD on send panda
other_panda.set_gmlan(True if obd else None)
# clear and flush
other_panda.can_clear(send_bus)
for recv_bus in recv_buses:
black_panda.can_clear(recv_bus)
black_panda.can_recv()
other_panda.can_recv()
# send the characters
at = random.randint(1, 2000)
st = get_test_string()[0:8]
other_panda.can_send(at, st, send_bus)
time.sleep(0.05)
# check for receive
cans_echo = other_panda.can_recv()
cans_loop = black_panda.can_recv()
loop_buses = []
for loop in cans_loop:
if (loop[0] != at) or (loop[2] != st):
content_errors += 1
loop_buses.append(loop[3])
# test loop buses
recv_buses.sort()
loop_buses.sort()
if(recv_buses != loop_buses):
return False
else:
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="Number of test iterations to run")
parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0)
args = parser.parse_args()
if args.n is None:
while True:
run_test(sleep_duration=args.sleep)
else:
for i in range(args.n):
run_test(sleep_duration=args.sleep)

@ -12,33 +12,38 @@ setcolor = ["\033[1;32;40m", "\033[1;31;40m"]
unsetcolor = "\033[00m" unsetcolor = "\033[00m"
if __name__ == "__main__": if __name__ == "__main__":
port_number = int(os.getenv("PORT", 0)) while True:
claim = os.getenv("CLAIM") is not None try:
port_number = int(os.getenv("PORT", 0))
claim = os.getenv("CLAIM") is not None
serials = Panda.list() serials = Panda.list()
if os.getenv("SERIAL"): if os.getenv("SERIAL"):
serials = filter(lambda x: x==os.getenv("SERIAL"), serials) serials = filter(lambda x: x==os.getenv("SERIAL"), serials)
pandas = list(map(lambda x: Panda(x, claim=claim), serials)) pandas = list(map(lambda x: Panda(x, claim=claim), serials))
if not len(pandas): if not len(pandas):
sys.exit("no pandas found") sys.exit("no pandas found")
if os.getenv("BAUD") is not None: if os.getenv("BAUD") is not None:
for panda in pandas: for panda in pandas:
panda.set_uart_baud(port_number, int(os.getenv("BAUD"))) panda.set_uart_baud(port_number, int(os.getenv("BAUD")))
while True:
for i, panda in enumerate(pandas):
while True: while True:
ret = panda.serial_read(port_number) for i, panda in enumerate(pandas):
if len(ret) > 0: while True:
sys.stdout.write(setcolor[i] + str(ret) + unsetcolor) ret = panda.serial_read(port_number)
sys.stdout.flush() if len(ret) > 0:
else: sys.stdout.write(setcolor[i] + str(ret) + unsetcolor)
break sys.stdout.flush()
if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []): else:
ln = sys.stdin.readline() break
if claim: if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
panda.serial_write(port_number, ln) ln = sys.stdin.readline()
time.sleep(0.01) if claim:
panda.serial_write(port_number, ln)
time.sleep(0.01)
except:
print("panda disconnected!")
time.sleep(0.5);

@ -0,0 +1,19 @@
#!/usr/bin/env python
import time
from panda import Panda
if __name__ == "__main__":
panda_serials = Panda.list()
pandas = []
for ps in panda_serials:
pandas.append(Panda(serial=ps))
if len(pandas) == 0:
print("No pandas connected")
assert False
while True:
for panda in pandas:
print(panda.health())
print("\n")
time.sleep(0.5)

@ -57,8 +57,9 @@ void set_toyota_rt_torque_last(int t);
void init_tests_honda(void); void init_tests_honda(void);
bool get_honda_moving(void); bool get_honda_moving(void);
int get_honda_brake_prev(void); bool get_honda_brake_pressed_prev(void);
int get_honda_gas_prev(void); int get_honda_gas_prev(void);
void set_honda_fwd_brake(bool);
void set_honda_alt_brake_msg(bool); void set_honda_alt_brake_msg(bool);
void set_honda_bosch_hardware(bool); void set_honda_bosch_hardware(bool);
int get_honda_bosch_hardware(void); int get_honda_bosch_hardware(void);

@ -227,8 +227,8 @@ bool get_honda_moving(void){
return honda_moving; return honda_moving;
} }
int get_honda_brake_prev(void){ bool get_honda_brake_pressed_prev(void){
return honda_brake_prev; return honda_brake_pressed_prev;
} }
int get_honda_gas_prev(void){ int get_honda_gas_prev(void){
@ -247,6 +247,10 @@ int get_honda_bosch_hardware(void) {
return honda_bosch_hardware; return honda_bosch_hardware;
} }
void set_honda_fwd_brake(bool c){
honda_fwd_brake = c;
}
void init_tests(void){ void init_tests(void){
// get HW_TYPE from env variable set in test.sh // get HW_TYPE from env variable set in test.sh
hw_type = atoi(getenv("HW_TYPE")); hw_type = atoi(getenv("HW_TYPE"));
@ -315,8 +319,9 @@ void init_tests_subaru(void){
void init_tests_honda(void){ void init_tests_honda(void){
init_tests(); init_tests();
honda_moving = false; honda_moving = false;
honda_brake_prev = 0; honda_brake_pressed_prev = false;
honda_gas_prev = 0; honda_gas_prev = 0;
honda_fwd_brake = false;
} }
void set_gmlan_digital_output(int to_set){ void set_gmlan_digital_output(int to_set){

@ -64,7 +64,7 @@ class TestHondaSafety(unittest.TestCase):
def _send_brake_msg(self, brake): def _send_brake_msg(self, brake):
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *')
to_send[0].RIR = 0x1FA << 21 to_send[0].RIR = 0x1FA << 21
to_send[0].RDLR = ((brake & 0x3) << 8) | ((brake & 0x3FF) >> 2) to_send[0].RDLR = ((brake & 0x3) << 14) | ((brake & 0x3FF) >> 2)
return to_send return to_send
@ -112,9 +112,9 @@ class TestHondaSafety(unittest.TestCase):
self.assertEqual(1, self.safety.get_honda_moving()) self.assertEqual(1, self.safety.get_honda_moving())
def test_prev_brake(self): def test_prev_brake(self):
self.assertFalse(self.safety.get_honda_brake_prev()) self.assertFalse(self.safety.get_honda_brake_pressed_prev())
self.safety.safety_rx_hook(self._brake_msg(True)) self.safety.safety_rx_hook(self._brake_msg(True))
self.assertTrue(self.safety.get_honda_brake_prev()) self.assertTrue(self.safety.get_honda_brake_pressed_prev())
def test_disengage_on_brake(self): def test_disengage_on_brake(self):
self.safety.set_controls_allowed(1) self.safety.set_controls_allowed(1)
@ -204,17 +204,22 @@ class TestHondaSafety(unittest.TestCase):
self.safety.set_gas_interceptor_detected(False) self.safety.set_gas_interceptor_detected(False)
def test_brake_safety_check(self): def test_brake_safety_check(self):
for long_controls_allowed in [0, 1]: for fwd_brake in [False, True]:
self.safety.set_long_controls_allowed(long_controls_allowed) self.safety.set_honda_fwd_brake(fwd_brake)
for brake in np.arange(0, MAX_BRAKE + 10, 1): for long_controls_allowed in [0, 1]:
for controls_allowed in [True, False]: self.safety.set_long_controls_allowed(long_controls_allowed)
self.safety.set_controls_allowed(controls_allowed) for brake in np.arange(0, MAX_BRAKE + 10, 1):
if controls_allowed and long_controls_allowed: for controls_allowed in [True, False]:
send = MAX_BRAKE >= brake >= 0 self.safety.set_controls_allowed(controls_allowed)
else: if fwd_brake:
send = brake == 0 send = False # block openpilot brake msg when fwd'ing stock msg
self.assertEqual(send, self.safety.safety_tx_hook(self._send_brake_msg(brake))) elif controls_allowed and long_controls_allowed:
send = MAX_BRAKE >= brake >= 0
else:
send = brake == 0
self.assertEqual(send, self.safety.safety_tx_hook(self._send_brake_msg(brake)))
self.safety.set_long_controls_allowed(True) self.safety.set_long_controls_allowed(True)
self.safety.set_honda_fwd_brake(False)
def test_gas_interceptor_safety_check(self): def test_gas_interceptor_safety_check(self):
for long_controls_allowed in [0, 1]: for long_controls_allowed in [0, 1]:
@ -252,27 +257,33 @@ class TestHondaSafety(unittest.TestCase):
buss = range(0x0, 0x3) buss = range(0x0, 0x3)
msgs = range(0x1, 0x800) msgs = range(0x1, 0x800)
long_controls_allowed = [0, 1] long_controls_allowed = [0, 1]
fwd_brake = [False, True]
self.safety.set_honda_bosch_hardware(0) self.safety.set_honda_bosch_hardware(0)
for l in long_controls_allowed: for f in fwd_brake:
self.safety.set_long_controls_allowed(l) self.safety.set_honda_fwd_brake(f)
blocked_msgs = [0xE4, 0x194, 0x33D] for l in long_controls_allowed:
if l: self.safety.set_long_controls_allowed(l)
blocked_msgs += [0x1FA ,0x30C, 0x39F] blocked_msgs = [0xE4, 0x194, 0x33D]
for b in buss: if l:
for m in msgs: blocked_msgs += [0x30C, 0x39F]
if b == 0: if not f:
fwd_bus = 2 blocked_msgs += [0x1FA]
elif b == 1: for b in buss:
fwd_bus = -1 for m in msgs:
elif b == 2: if b == 0:
fwd_bus = -1 if m in blocked_msgs else 0 fwd_bus = 2
elif b == 1:
# assume len 8 fwd_bus = -1
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8))) elif b == 2:
fwd_bus = -1 if m in blocked_msgs else 0
# assume len 8
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, self._send_msg(b, m, 8)))
self.safety.set_long_controls_allowed(True) self.safety.set_long_controls_allowed(True)
self.safety.set_honda_fwd_brake(False)

Loading…
Cancel
Save