Skip to content

Commit

Permalink
Merge pull request commaai#34 from diamondman/feature/ELM327_support
Browse files Browse the repository at this point in the history
Feature/elm327 support
  • Loading branch information
edsammy authored Aug 21, 2017
2 parents 71d81ed + e28f6f8 commit 7de0385
Show file tree
Hide file tree
Showing 12 changed files with 2,797 additions and 20 deletions.
7 changes: 6 additions & 1 deletion board/drivers/can.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ int can_push(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) {
return ret;
}

void can_clear(can_ring *q) {
enter_critical_section();
q->w_ptr = 0;
q->r_ptr = 0;
exit_critical_section();
}

// assign CAN numbering
// bus num: Can bus number on ODB connector. Sent to/from USB
Expand Down Expand Up @@ -393,4 +399,3 @@ void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number) {
void can_set_forwarding(int from, int to) {
can_forwarding[from] = to;
}

12 changes: 10 additions & 2 deletions board/drivers/uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ int putc(uart_ring *q, char elem) {
return ret;
}

void clear_uart_buff(uart_ring *q) {
enter_critical_section();
q->w_ptr_tx = 0;
q->r_ptr_tx = 0;
q->w_ptr_rx = 0;
q->r_ptr_rx = 0;
exit_critical_section();
}

// ***************************** start UART code *****************************

#define __DIV(_PCLK_, _BAUD_) (((_PCLK_)*25)/(4*(_BAUD_)))
Expand All @@ -146,7 +155,7 @@ void uart_init(USART_TypeDef *u, int baud) {
// enable uart and tx+rx mode
u->CR1 = USART_CR1_UE;
uart_set_baud(u, baud);

u->CR1 |= USART_CR1_TE | USART_CR1_RE;
//u->CR2 = USART_CR2_STOP_0 | USART_CR2_STOP_1;
//u->CR2 = USART_CR2_STOP_0;
Expand Down Expand Up @@ -213,4 +222,3 @@ void hexdump(const void *a, int l) {
}
puts("\n");
}

27 changes: 23 additions & 4 deletions board/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,11 @@ int usb_cb_ep1_in(uint8_t *usbdata, int len, int hardwired) {

// send on serial, first byte to select the ring
void usb_cb_ep2_out(uint8_t *usbdata, int len, int hardwired) {
int i;
if (len == 0) return;
uart_ring *ur = get_ring_by_number(usbdata[0]);
if (!ur) return;
if ((usbdata[0] < 2) || safety_tx_lin_hook(usbdata[0]-2, usbdata+1, len-1)) {
for (i = 1; i < len; i++) while (!putc(ur, usbdata[i]));
for (int i = 1; i < len; i++) while (!putc(ur, usbdata[i]));
}
}

Expand Down Expand Up @@ -251,7 +250,8 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, int hardwired) {
case 0xdc:
// this is the only way to leave silent mode
// and it's blocked over WiFi
if (hardwired) {
// Allow ELM security mode to be set over wifi.
if (hardwired || setup->b.wValue.w == SAFETY_ELM327) {
safety_set_mode(setup->b.wValue.w);
can_silent = (setup->b.wValue.w == SAFETY_NOOUTPUT);
can_init_all();
Expand Down Expand Up @@ -358,6 +358,26 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, int hardwired) {

delay(140 * 9000);
break;
// **** 0xf1: Clear CAN ring buffer.
case 0xf1:
if (setup->b.wValue.w == 0xFFFF) {
puts("Clearing CAN Rx queue\n");
can_clear(&can_rx_q);
} else if (setup->b.wValue.w < BUS_MAX) {
puts("Clearing CAN Tx queue\n");
can_clear(can_queues[setup->b.wValue.w]);
}
break;
// **** 0xf2: Clear UART ring buffer.
case 0xf2:
{
uart_ring * rb = get_ring_by_number(setup->b.wValue.w);
if (rb) {
puts("Clearing UART queue.\n");
clear_uart_buff(rb);
}
break;
}
default:
puts("NO HANDLER ");
puth(setup->b.bRequest);
Expand Down Expand Up @@ -508,4 +528,3 @@ int main() {

return 0;
}

6 changes: 4 additions & 2 deletions board/safety.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ int controls_allowed = 0;
// Include the actual safety policies.
#include "safety/safety_defaults.h"
#include "safety/safety_honda.h"
#include "safety/safety_elm327.h"

const safety_hooks *current_hooks = &nooutput_hooks;

Expand All @@ -43,11 +44,13 @@ typedef struct {
#define SAFETY_NOOUTPUT 0
#define SAFETY_HONDA 1
#define SAFETY_ALLOUTPUT 0x1337
#define SAFETY_ELM327 0xE327

const safety_hook_config safety_hook_registry[] = {
{SAFETY_NOOUTPUT, &nooutput_hooks},
{SAFETY_HONDA, &honda_hooks},
{SAFETY_ALLOUTPUT, &alloutput_hooks},
{SAFETY_ELM327, &elm327_hooks},
};

#define HOOK_CONFIG_COUNT (sizeof(safety_hook_registry)/sizeof(safety_hook_config))
Expand All @@ -56,10 +59,9 @@ int safety_set_mode(uint16_t mode){
for (int i = 0; i < HOOK_CONFIG_COUNT; i++) {
if (safety_hook_registry[i].id == mode) {
current_hooks = safety_hook_registry[i].hooks;
current_hooks->init();
if(current_hooks->init) current_hooks->init();
return 0;
}
}
return -1;
}

35 changes: 35 additions & 0 deletions board/safety/safety_elm327.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
static void elm327_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) {}

static int elm327_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) {
//All ELM traffic must appear on CAN0
if(((to_send->RDTR >> 4) & 0xf) != 0) return 0;
//All ISO 15765-4 messages must be 8 bytes long
if((to_send->RDTR & 0xf) != 8) return 0;

if(to_send->RIR & 4){
uint32_t addr = to_send->RIR >> 3;
//Check valid 29 bit send addresses for ISO 15765-4
if(!(addr == 0x18DB33F1 || (addr & 0x1FFF00FF) == 0x18DA00F1)) return 0;
} else {
uint32_t addr = to_send->RIR >> 21;
//Check valid 11 bit send addresses for ISO 15765-4
if(!(addr == 0x7DF || (addr & 0x7F8) == 0x7E0)) return 0;
}

return true;
}

static int elm327_tx_lin_hook(int lin_num, uint8_t *data, int len) {
if(lin_num != 0) return false; //Only operate on LIN 0, aka serial 2
if(len < 5 || len > 11) return false; //Valid KWP size
if(!((data[0] & 0xF8) == 0xC0 && (data[0] & 0x07) > 0 &&
data[1] == 0x33 && data[2] == 0xF1)) return false; //Bad msg
return true;
}

const safety_hooks elm327_hooks = {
.init = NULL,
.rx = elm327_rx_hook,
.tx = elm327_tx_hook,
.tx_lin = elm327_tx_lin_hook,
};
101 changes: 101 additions & 0 deletions boardesp/ELM327.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
ELM327 support for panda
======
The panda now has basic ELM327 support.

### What is ELM327?

ELM327 is a command protocol for interfacing with cars using an OBD-II
port to read [list](standard vehicle diagnostic codes). ELM327
originally referred to a line of programmable microcontrollers that
implemented the ELM327 command protocol, and are still being
developed.

ELM327 devices present a shell and commands are sent via a UART. The
official ELM327 chips only support raw UART communication, but most
devices built with the ELM327 devices (official or clones) expose the
UART in a more modern way (Wifi, USB, etc).

Mechanics use ELM to diagnose vehicles, and reset fault codes in the
car's computer after fixing the issue (turning off the dreaded check
engine light). Car owners can use ELM devices to perform the same
diagnostics in their garage using either a raw terminal to send
commands to the ELM device directly, or using a GUI (like one of
several popular smart phone apps) to translate the OBD error codes
into readable messages. These GUIs also often allow monitoring of the
performance of the car's speed, engine rpm, etc.

The panda natively supports sending all the important OBD diagnostic
messages, but ELM327 support removes the need for the user to manually
craft CAN or LIN packets using the native panda API, and grants
compatibility with many existing tools.

[Wikipedia](https://en.wikipedia.org/wiki/ELM327) can provide
additional information.

### OBD Protocols?

While the commands that the OBD standard describe are in fact
standard, there are several different protocols that those messages
can be sent and received with. All cars after 1991 support one of
these protocols. Which one depends on the car's year and country, as
legal requirements change over the years.

The panda supports the most popular/modern of these protocols, and all
but two can be added as needed. Below is a chart of the OBD-II
protocols supported by the panda.

| Protocol | Support Status |
| --- | --- |
| SAE J1850 PWM (41.6 kbit/s) | Never/Obsolete |
| SAE J1850 VPW (10.4 kbit/s) | Never/Obsolete |
| ISO 9141-2 (5 baud init, 10.4 kbit/s) | Unsupported |
| ISO 14230-4 KWP (5 baud init, 10.4 kbit/s) | Unsupported |
| ISO 14230-4 KWP (fast init, 10.4 kbit/s) | Supported |
| ISO 15765-4 CAN (11 bit ID, 500 kbit/s) | Supported |
| ISO 15765-4 CAN (29 bit ID, 500 kbit/s) | Supported |
| ISO 15765-4 CAN (11 bit ID, 250 kbit/s) | Supported |
| ISO 15765-4 CAN (29 bit ID, 250 kbit/s) | Supported |
| SAE J1939 (250kbps) | Unsupported |

### The Implementation

The panda ELM327 implementation is not a full implementation of all
the features of the official ELM327 microcontroller. Like most ELM327
clones, the panda reports its ELM version as the unreleased version
1.5, despite only implementing commands from protocol version 1.0.


### Testing

These tests require two pandas. One to be tested, and the second to
simulate the vehicle.

The panda used to simulate the vehicle must be plugged into a USB port
of the testing computer.

The computer running the tests must be connected to the panda being
tested's wifi network.

The following command will run the tests (nosetest should work fine
instead of pytest if you still prefer using that). The CANSIMSERIAL
environment variable will force the car simulator to use the correct
panda as the simulator if multiple pandas are attached via usb to the
host computer.

```
CANSIMSERIAL=car_sim_panda_serial pytest tests/automated/elm_wifi.py
```

A single test can be run by putting the test name after the file name
and two colons, like so:

```
CANSIMSERIAL=car_sim_panda_serial pytest tests/automated/elm_wifi.py::test_important_thing
```

For more detail, provide the -s (show output) and the -vv (very
verbose) flags.

```
CANSIMSERIAL=car_sim_panda_serial pytest -s -vv tests/automated/elm_wifi.py
```
Loading

0 comments on commit 7de0385

Please sign in to comment.