You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					1577 lines
				
				50 KiB
			
		
		
			
		
	
	
					1577 lines
				
				50 KiB
			| 
								 
											8 years ago
										 
									 | 
							
								#include "ets_sys.h"
							 | 
						||
| 
								 | 
							
								#include "osapi.h"
							 | 
						||
| 
								 | 
							
								#include "gpio.h"
							 | 
						||
| 
								 | 
							
								#include "os_type.h"
							 | 
						||
| 
								 | 
							
								#include "user_interface.h"
							 | 
						||
| 
								 | 
							
								#include "espconn.h"
							 | 
						||
| 
								 | 
							
								#include "mem.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "driver/uart.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//#define ELM_DEBUG
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define min(a,b) ((a) < (b) ? (a) : (b))
							 | 
						||
| 
								 | 
							
								#define max(a,b) ((a) > (b) ? (a) : (b))
							 | 
						||
| 
								 | 
							
								int ICACHE_FLASH_ATTR spi_comm(char *dat, int len, uint32_t *recvData, int recvDataLen);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define ELM_PORT 35000
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//Version 1.5 is an invalid version used by many pirate clones
							 | 
						||
| 
								 | 
							
								//that only partially support 1.0.
							 | 
						||
| 
								 | 
							
								#define IDENT_MSG "ELM327 v1.5\r\r"
							 | 
						||
| 
								 | 
							
								#define DEVICE_DESC "Panda\n\n"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define SHOW_CONNECTION(msg, p_conn) os_printf("%s %p, proto %p, %d.%d.%d.%d:%d disconnect\r\n", \
							 | 
						||
| 
								 | 
							
								        msg, p_conn, (p_conn)->proto.tcp, (p_conn)->proto.tcp->remote_ip[0], \
							 | 
						||
| 
								 | 
							
								        (p_conn)->proto.tcp->remote_ip[1], (p_conn)->proto.tcp->remote_ip[2], \
							 | 
						||
| 
								 | 
							
								        (p_conn)->proto.tcp->remote_ip[3], (p_conn)->proto.tcp->remote_port)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const static char hex_lookup[] = {'0', '1', '2', '3', '4', '5', '6', '7',
							 | 
						||
| 
								 | 
							
								                                  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct __attribute__((packed)) {
							 | 
						||
| 
								 | 
							
								  bool tx       : 1;
							 | 
						||
| 
								 | 
							
								  bool          : 1;
							 | 
						||
| 
								 | 
							
								  bool ext      : 1;
							 | 
						||
| 
								 | 
							
								  uint32_t addr : 29;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  uint8_t len   : 4;
							 | 
						||
| 
								 | 
							
								  uint8_t bus   : 8;
							 | 
						||
| 
								 | 
							
								  uint8_t       : 4; //unused
							 | 
						||
| 
								 | 
							
								  uint16_t ts   : 16;
							 | 
						||
| 
								 | 
							
								  uint8_t data[8];
							 | 
						||
| 
								 | 
							
								} panda_can_msg_t;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//TODO: Masking is likely unnecessary for these bit fields. Check.
							 | 
						||
| 
								 | 
							
								#define panda_get_can_addr(recv) (((recv)->ext) ? ((recv)->addr & 0x1FFFFFFF) :\
							 | 
						||
| 
								 | 
							
								                                  (((recv)->addr >> 18) & 0x7FF))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define PANDA_CAN_FLAG_TRANSMIT 1
							 | 
						||
| 
								 | 
							
								#define PANDA_CAN_FLAG_EXTENDED 4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define PANDA_USB_CAN_WRITE_BUS_NUM 3
							 | 
						||
| 
								 | 
							
								#define PANDA_USB_LIN_WRITE_BUS_NUM 2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct _elm_tcp_conn {
							 | 
						||
| 
								 | 
							
								  struct espconn *conn;
							 | 
						||
| 
								 | 
							
								  struct _elm_tcp_conn *next;
							 | 
						||
| 
								 | 
							
								} elm_tcp_conn_t;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct __attribute__((packed)) {
							 | 
						||
| 
								 | 
							
								  uint8_t len;
							 | 
						||
| 
								 | 
							
								  uint8_t dat[7]; //mode and data
							 | 
						||
| 
								 | 
							
								} elm_can_obd_msg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct __attribute__((packed)) {
							 | 
						||
| 
								 | 
							
								  uint8_t priority;
							 | 
						||
| 
								 | 
							
								  uint8_t receiver;
							 | 
						||
| 
								 | 
							
								  uint8_t sender;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  uint8_t dat[8]; //mode, data, and checksum
							 | 
						||
| 
								 | 
							
								} elm_lin_obd_msg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct __attribute__((packed)) {
							 | 
						||
| 
								 | 
							
								  uint16_t usb_ep_num;
							 | 
						||
| 
								 | 
							
								  uint16_t payload_len;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  uint8_t serial_port;
							 | 
						||
| 
								 | 
							
								  //uint8_t msg[8+3];
							 | 
						||
| 
								 | 
							
								  elm_lin_obd_msg msg;
							 | 
						||
| 
								 | 
							
								} elm_lin_usb_msg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static struct espconn elm_conn;
							 | 
						||
| 
								 | 
							
								static esp_tcp elm_proto;
							 | 
						||
| 
								 | 
							
								static elm_tcp_conn_t *connection_list = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static char stripped_msg[0x100];
							 | 
						||
| 
								 | 
							
								static uint16 stripped_msg_len = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static char in_msg[0x100];
							 | 
						||
| 
								 | 
							
								static uint16 in_msg_len = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static char rsp_buff[536]; //TCP min MTU
							 | 
						||
| 
								 | 
							
								static uint16 rsp_buff_len = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static uint8_t pandaSendData[0x14] = {0};
							 | 
						||
| 
								 | 
							
								static uint32_t pandaRecvData[0x40] = {0};
							 | 
						||
| 
								 | 
							
								static uint32_t pandaRecvDataDummy[0x40] = {0}; // Used for CAN write operations (no received data)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define ELM_MODE_SELECTED_PROTOCOL_DEFAULT 6
							 | 
						||
| 
								 | 
							
								#define ELM_MODE_TIMEOUT_DEFAULT 20;
							 | 
						||
| 
								 | 
							
								#define ELM_MODE_KEEPALIVE_PERIOD_DEFAULT (0x92*20)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static bool elm_mode_echo = true;
							 | 
						||
| 
								 | 
							
								static bool elm_mode_linefeed = false;
							 | 
						||
| 
								 | 
							
								static bool elm_mode_additional_headers = false;
							 | 
						||
| 
								 | 
							
								static bool elm_mode_auto_protocol = true;
							 | 
						||
| 
								 | 
							
								static uint8_t elm_selected_protocol = ELM_MODE_SELECTED_PROTOCOL_DEFAULT;
							 | 
						||
| 
								 | 
							
								static bool elm_mode_print_spaces = true;
							 | 
						||
| 
								 | 
							
								static uint8_t elm_mode_adaptive_timing = 1;
							 | 
						||
| 
								 | 
							
								static bool elm_mode_allow_long = false;
							 | 
						||
| 
								 | 
							
								static uint16_t elm_mode_timeout = ELM_MODE_TIMEOUT_DEFAULT;
							 | 
						||
| 
								 | 
							
								static uint16_t elm_mode_keepalive_period = ELM_MODE_KEEPALIVE_PERIOD_DEFAULT;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool lin_bus_initialized = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/***********************************************
							 | 
						||
| 
								 | 
							
								 ***       ELM CLI response functions        ***
							 | 
						||
| 
								 | 
							
								 *** (for sending data back to the terminal) ***
							 | 
						||
| 
								 | 
							
								 ***********************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// All ELM operations are global, so send data out to all connections
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_tcp_tx_flush() {
							 | 
						||
| 
								 | 
							
								  if(!rsp_buff_len) return; // Was causing small error messages
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for(elm_tcp_conn_t *iter = connection_list; iter != NULL; iter = iter->next){
							 | 
						||
| 
								 | 
							
								    int8_t err = espconn_send(iter->conn, rsp_buff, rsp_buff_len);
							 | 
						||
| 
								 | 
							
								    if(err){
							 | 
						||
| 
								 | 
							
								      os_printf("  Wifi %p TX error code %d\n", iter->conn, err);
							 | 
						||
| 
								 | 
							
								      if(err == ESPCONN_ARG) {
							 | 
						||
| 
								 | 
							
								        if(iter == connection_list) {
							 | 
						||
| 
								 | 
							
								          connection_list = iter->next;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          for(elm_tcp_conn_t *iter2 = connection_list; iter2 != NULL; iter2 = iter2->next)
							 | 
						||
| 
								 | 
							
								            if(iter2->next == iter) {
							 | 
						||
| 
								 | 
							
								              iter2->next = iter->next;
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        os_printf("  deleting orphaned connection. iter: %p; conn: %p\n", iter, iter->conn);
							 | 
						||
| 
								 | 
							
								        os_free(iter);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  rsp_buff_len = 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_append_rsp(const char *data, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  uint16_t overflow_len = 0;
							 | 
						||
| 
								 | 
							
								  if(rsp_buff_len + len > sizeof(rsp_buff)) {
							 | 
						||
| 
								 | 
							
								    overflow_len = rsp_buff_len + len - sizeof(rsp_buff);
							 | 
						||
| 
								 | 
							
								    len = sizeof(rsp_buff) - rsp_buff_len;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if(!elm_mode_linefeed) {
							 | 
						||
| 
								 | 
							
								    memcpy(rsp_buff + rsp_buff_len, data, len);
							 | 
						||
| 
								 | 
							
								    rsp_buff_len += len;
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    for(int i=0; i < len && rsp_buff_len < sizeof(rsp_buff); i++){
							 | 
						||
| 
								 | 
							
								      rsp_buff[rsp_buff_len++] = data[i];
							 | 
						||
| 
								 | 
							
								      if(data[i] == '\r' && rsp_buff_len < sizeof(rsp_buff))
							 | 
						||
| 
								 | 
							
								        rsp_buff[rsp_buff_len++] = '\n';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if(overflow_len) {
							 | 
						||
| 
								 | 
							
								    os_printf("Packet full, sending\n");
							 | 
						||
| 
								 | 
							
								    elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								    elm_append_rsp(data + len, overflow_len);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define elm_append_rsp_const(str) elm_append_rsp(str, sizeof(str)-1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_append_rsp_hex_byte(uint8_t num) {
							 | 
						||
| 
								 | 
							
								  elm_append_rsp(&hex_lookup[num >> 4], 1);
							 | 
						||
| 
								 | 
							
								  elm_append_rsp(&hex_lookup[num & 0xF], 1);
							 | 
						||
| 
								 | 
							
								  if(elm_mode_print_spaces) elm_append_rsp_const(" ");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_append_rsp_can_msg_addr(const panda_can_msg_t *recv) {
							 | 
						||
| 
								 | 
							
								  //Show address
							 | 
						||
| 
								 | 
							
								  uint32_t addr = panda_get_can_addr(recv);
							 | 
						||
| 
								 | 
							
								  if(recv->ext){
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_hex_byte(addr>>24);
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_hex_byte(addr>>16);
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_hex_byte(addr>>8);
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_hex_byte(addr);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    elm_append_rsp(&hex_lookup[addr>>8], 1);
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_hex_byte(addr);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/***************************************
							 | 
						||
| 
								 | 
							
								 ***  Panda communication functions  ***
							 | 
						||
| 
								 | 
							
								 *** (for controlling the Panda MCU) ***
							 | 
						||
| 
								 | 
							
								 ***************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int ICACHE_FLASH_ATTR panda_usbemu_ctrl_write(uint8_t request_type, uint8_t request,
							 | 
						||
| 
								 | 
							
								                                                     uint16_t value, uint16_t index, uint16_t length) {
							 | 
						||
| 
								 | 
							
								  //self.sock.send(struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length));
							 | 
						||
| 
								 | 
							
								  *(uint16_t*)(pandaSendData) = 0;
							 | 
						||
| 
								 | 
							
								  *(uint16_t*)(pandaSendData+2) = 0;
							 | 
						||
| 
								 | 
							
								  pandaSendData[4] = request_type;
							 | 
						||
| 
								 | 
							
								  pandaSendData[5] = request;
							 | 
						||
| 
								 | 
							
								  *(uint16_t*)(pandaSendData+6) = value;
							 | 
						||
| 
								 | 
							
								  *(uint16_t*)(pandaSendData+8) = index;
							 | 
						||
| 
								 | 
							
								  *(uint16_t*)(pandaSendData+10) = length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  int returned_count = spi_comm(pandaSendData, 0x10, pandaRecvData, 0x40);
							 | 
						||
| 
								 | 
							
								  if(returned_count > 0x40 || returned_count < 0)
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  return returned_count;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define panda_set_can0_cbaud(cbps) panda_usbemu_ctrl_write(0x40, 0xde, 0, cbps, 0)
							 | 
						||
| 
								 | 
							
								#define panda_set_can0_kbaud(kbps) panda_usbemu_ctrl_write(0x40, 0xde, 0, kbps*10, 0)
							 | 
						||
| 
								 | 
							
								#define panda_set_safety_mode(mode) panda_usbemu_ctrl_write(0x40, 0xdc, mode, 0, 0)
							 | 
						||
| 
								 | 
							
								#define panda_kline_wakeup_pulse() panda_usbemu_ctrl_write(0x40, 0xf0, 0, 0, 0)
							 | 
						||
| 
								 | 
							
								#define panda_clear_can_rx() panda_usbemu_ctrl_write(0x40, 0xf1, 0xFFFF, 0, 0)
							 | 
						||
| 
								 | 
							
								#define panda_clear_lin_txrx() panda_usbemu_ctrl_write(0x40, 0xf2, 2, 0, 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int ICACHE_FLASH_ATTR panda_usbemu_can_read(panda_can_msg_t** can_msgs) {
							 | 
						||
| 
								 | 
							
								  int returned_count = spi_comm((uint8_t *)((const uint16 []){1,0}), 4, pandaRecvData, 0x40);
							 | 
						||
| 
								 | 
							
								  if(returned_count > 0x40 || returned_count < 0){
							 | 
						||
| 
								 | 
							
								    os_printf("CAN read got invalid length\n");
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  *can_msgs = (panda_can_msg_t*)(pandaRecvData+1);
							 | 
						||
| 
								 | 
							
								  return returned_count/sizeof(panda_can_msg_t);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int ICACHE_FLASH_ATTR panda_usbemu_can_write(bool ext, uint32_t addr,
							 | 
						||
| 
								 | 
							
								                                                    char *candata, uint8_t canlen) {
							 | 
						||
| 
								 | 
							
								  uint32_t rir;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(canlen > 8) return 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(ext || addr >= 0x800){
							 | 
						||
| 
								 | 
							
								    rir = (addr << 3) | PANDA_CAN_FLAG_TRANSMIT | PANDA_CAN_FLAG_EXTENDED;
							 | 
						||
| 
								 | 
							
								  }else{
							 | 
						||
| 
								 | 
							
								    rir = (addr << 21) | PANDA_CAN_FLAG_TRANSMIT;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #define MAX_CAN_LEN 8
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //Wifi USB Wrapper
							 | 
						||
| 
								 | 
							
								  *(uint16_t*)(pandaSendData) = PANDA_USB_CAN_WRITE_BUS_NUM; //USB Bulk Endpoint ID.
							 | 
						||
| 
								 | 
							
								  *(uint16_t*)(pandaSendData+2) = MAX_CAN_LEN;
							 | 
						||
| 
								 | 
							
								  //BULK MESSAGE
							 | 
						||
| 
								 | 
							
								  *(uint32_t*)(pandaSendData+4) = rir;
							 | 
						||
| 
								 | 
							
								  *(uint32_t*)(pandaSendData+8) = MAX_CAN_LEN | (0 << 4); //0 is CAN bus number.
							 | 
						||
| 
								 | 
							
								  //CAN DATA
							 | 
						||
| 
								 | 
							
								  memcpy(pandaSendData+12, candata, canlen);
							 | 
						||
| 
								 | 
							
								  memset(pandaSendData+12+canlen, 0, MAX_CAN_LEN-canlen);
							 | 
						||
| 
								 | 
							
								  for(int i = 12+canlen; i < 20; i++) pandaSendData[i] = 0; //Zero the rest
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /* spi_comm will erase data in the recv buffer even if you are only
							 | 
						||
| 
								 | 
							
								   * interested in sending data that gets no response (like writing
							 | 
						||
| 
								 | 
							
								   * can data). This behavior becomes problematic when trying to send
							 | 
						||
| 
								 | 
							
								   * a can message while processsing received can messages. A dummy
							 | 
						||
| 
								 | 
							
								   * recv buffer is used here so received data is not overwritten. */
							 | 
						||
| 
								 | 
							
								  int returned_count = spi_comm(pandaSendData, 0x14, pandaRecvDataDummy, 0x40);
							 | 
						||
| 
								 | 
							
								  if(returned_count)
							 | 
						||
| 
								 | 
							
								    os_printf("ELM Can send expected 0 bytes back from panda. Got %d bytes instead\n", returned_count);
							 | 
						||
| 
								 | 
							
								  if(returned_count > 0x40) return 0;
							 | 
						||
| 
								 | 
							
								  return returned_count;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								elm_lin_obd_msg lin_last_sent_msg;
							 | 
						||
| 
								 | 
							
								uint16_t lin_last_sent_msg_len = 0;
							 | 
						||
| 
								 | 
							
								bool lin_await_msg_echo = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int ICACHE_FLASH_ATTR panda_usbemu_kline_read(uint16_t len) {
							 | 
						||
| 
								 | 
							
								  int returned_count = panda_usbemu_ctrl_write(0xC0, 0xE0, 2, 0, len);
							 | 
						||
| 
								 | 
							
								  if(returned_count > len || returned_count < 0){
							 | 
						||
| 
								 | 
							
								    os_printf("LIN read got invalid length\n");
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  if(returned_count) {
							 | 
						||
| 
								 | 
							
								    os_printf("LIN Received %d bytes\n", returned_count);
							 | 
						||
| 
								 | 
							
								    os_printf("    Data: ");
							 | 
						||
| 
								 | 
							
								    for(int i = 0; i < returned_count; i++)
							 | 
						||
| 
								 | 
							
								      os_printf("%02x ", ((char*)(pandaRecvData+1))[i]);
							 | 
						||
| 
								 | 
							
								    os_printf("\n");
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								  return returned_count;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int ICACHE_FLASH_ATTR panda_usbemu_kline_write(elm_lin_obd_msg *msg) {
							 | 
						||
| 
								 | 
							
								  elm_lin_usb_msg usb_msg = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  usb_msg.usb_ep_num = PANDA_USB_LIN_WRITE_BUS_NUM; //USB Bulk Endpoint ID.
							 | 
						||
| 
								 | 
							
								  usb_msg.payload_len = (msg->priority & 0x07) + 4 + 1; //The +1 is for serial_port
							 | 
						||
| 
								 | 
							
								  usb_msg.serial_port = 2;
							 | 
						||
| 
								 | 
							
								  memcpy(&usb_msg.msg, msg, sizeof(elm_lin_obd_msg));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /* spi_comm will erase data in the recv buffer even if you are only
							 | 
						||
| 
								 | 
							
								   * interested in sending data that gets no response (like writing
							 | 
						||
| 
								 | 
							
								   * can data). This behavior becomes problematic when trying to send
							 | 
						||
| 
								 | 
							
								   * a can message while processsing received can messages. A dummy
							 | 
						||
| 
								 | 
							
								   * recv buffer is used here so received data is not overwritten. */
							 | 
						||
| 
								 | 
							
								  int returned_count = spi_comm((char*)&usb_msg, sizeof(elm_lin_usb_msg), pandaRecvDataDummy, 0x40);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(returned_count)
							 | 
						||
| 
								 | 
							
								    os_printf("ELM LIN send expected 0 bytes back from panda. Got %d bytes instead\n", returned_count);
							 | 
						||
| 
								 | 
							
								  if(returned_count > 0x40) return 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return returned_count;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/****************************************
							 | 
						||
| 
								 | 
							
								 *** Ringbuffer                       ***
							 | 
						||
| 
								 | 
							
								 ****************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//LIN data is delivered in chunks of arbitrary size. Using a
							 | 
						||
| 
								 | 
							
								//ringbuffer to handle it.
							 | 
						||
| 
								 | 
							
								uint8_t lin_ringbuff[0x20];
							 | 
						||
| 
								 | 
							
								uint8_t lin_ringbuff_start = 0;
							 | 
						||
| 
								 | 
							
								uint8_t lin_ringbuff_end = 0;
							 | 
						||
| 
								 | 
							
								#define lin_ringbuff_len \
							 | 
						||
| 
								 | 
							
								  (((sizeof(lin_ringbuff) + lin_ringbuff_end) - lin_ringbuff_start)% sizeof(lin_ringbuff))
							 | 
						||
| 
								 | 
							
								#define lin_ringbuff_get(index) (lin_ringbuff[(lin_ringbuff_start + index) % sizeof(lin_ringbuff)])
							 | 
						||
| 
								 | 
							
								#define lin_ringbuff_consume(len) lin_ringbuff_start = ((lin_ringbuff_start + len) % sizeof(lin_ringbuff))
							 | 
						||
| 
								 | 
							
								#define lin_ringbuff_clear()\
							 | 
						||
| 
								 | 
							
								  {lin_ringbuff_start = 0;  \
							 | 
						||
| 
								 | 
							
								  lin_ringbuff_end = 0;}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int ICACHE_FLASH_ATTR elm_LIN_ringbuff_memcmp(uint8_t *data, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  if(len > lin_ringbuff_len) return 1;
							 | 
						||
| 
								 | 
							
								  for(int i = 0; i < len; i++)
							 | 
						||
| 
								 | 
							
								    if(lin_ringbuff_get(i) != data[i]) return 1;
							 | 
						||
| 
								 | 
							
								  return 0; // Going with memcpy ret format where 0 means 'equal'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								uint16_t ICACHE_FLASH_ATTR elm_LIN_read_into_ringbuff() {
							 | 
						||
| 
								 | 
							
								  int bytelen = panda_usbemu_kline_read((sizeof(lin_ringbuff) - lin_ringbuff_len) - 1);
							 | 
						||
| 
								 | 
							
								  if(bytelen < 0) return 0;
							 | 
						||
| 
								 | 
							
								  for(int j = 0; j < bytelen; j++) {
							 | 
						||
| 
								 | 
							
								    lin_ringbuff[lin_ringbuff_end % sizeof(lin_ringbuff)] = ((char*)(pandaRecvData+1))[j];
							 | 
						||
| 
								 | 
							
								    lin_ringbuff_end = (lin_ringbuff_end + 1) % sizeof(lin_ringbuff);
							 | 
						||
| 
								 | 
							
								    if(lin_ringbuff_start == lin_ringbuff_end) lin_ringbuff_start++;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  if(bytelen){
							 | 
						||
| 
								 | 
							
								    os_printf("    RB Data (%d %d %d): ", lin_ringbuff_start, lin_ringbuff_end, lin_ringbuff_len);
							 | 
						||
| 
								 | 
							
								    for(int i = 0; i < sizeof(lin_ringbuff); i++)
							 | 
						||
| 
								 | 
							
								      os_printf("%02x ", lin_ringbuff[i]);
							 | 
						||
| 
								 | 
							
								    os_printf("\n");
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return bytelen;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/****************************************
							 | 
						||
| 
								 | 
							
								 *** String parsing utility functions ***
							 | 
						||
| 
								 | 
							
								 ****************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int8_t ICACHE_FLASH_ATTR elm_decode_hex_char(char b){
							 | 
						||
| 
								 | 
							
								  if(b >= '0' && b <= '9') return b - '0';
							 | 
						||
| 
								 | 
							
								  if(b >= 'A' && b <= 'F') return (b - 'A') + 10;
							 | 
						||
| 
								 | 
							
								  if(b >= 'a' && b <= 'f') return (b - 'a') + 10;
							 | 
						||
| 
								 | 
							
								  return -1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static uint8_t ICACHE_FLASH_ATTR elm_decode_hex_byte(const char* data) {
							 | 
						||
| 
								 | 
							
								  return (elm_decode_hex_char(data[0]) << 4) | elm_decode_hex_char(data[1]);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static bool ICACHE_FLASH_ATTR elm_check_valid_hex_chars(const char* data, uint8_t len) {
							 | 
						||
| 
								 | 
							
								  for(int i = 0; i < len; i++){
							 | 
						||
| 
								 | 
							
								    char b = data[i];
							 | 
						||
| 
								 | 
							
								    if(!((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')))
							 | 
						||
| 
								 | 
							
								      return 0;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return 1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static uint16_t ICACHE_FLASH_ATTR elm_strip(const char *data, uint16_t lenin,
							 | 
						||
| 
								 | 
							
								                                            char *outbuff, uint16_t outbufflen) {
							 | 
						||
| 
								 | 
							
								  uint16_t count = 0;
							 | 
						||
| 
								 | 
							
								  for(uint16_t i = 0; i < lenin; i++) {
							 | 
						||
| 
								 | 
							
								    if(count >= outbufflen) break;
							 | 
						||
| 
								 | 
							
								    if(data[i] == ' ') continue;
							 | 
						||
| 
								 | 
							
								    if(data[i] >= 'a' && data[i] <= 'z'){
							 | 
						||
| 
								 | 
							
								      outbuff[count++] = data[i] - ('a' - 'A');
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      outbuff[count++] = data[i];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if(data[i] == '\r') break;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return count;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int ICACHE_FLASH_ATTR elm_msg_find_cr_or_eos(char *data, uint16_t len){
							 | 
						||
| 
								 | 
							
								  uint16_t i;
							 | 
						||
| 
								 | 
							
								  for(i = 0; i < len; i++)
							 | 
						||
| 
								 | 
							
								    if(data[i] == '\r') {
							 | 
						||
| 
								 | 
							
								      i++;
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  return i;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*****************************************************
							 | 
						||
| 
								 | 
							
								 *** ELM protocol specification and implementation ***
							 | 
						||
| 
								 | 
							
								 *****************************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef enum {
							 | 
						||
| 
								 | 
							
								  AUTO, LIN, CAN11, CAN29, NA
							 | 
						||
| 
								 | 
							
								} elm_proto_type_t;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct elm_protocol {
							 | 
						||
| 
								 | 
							
								  bool supported;
							 | 
						||
| 
								 | 
							
								  elm_proto_type_t type;
							 | 
						||
| 
								 | 
							
								  uint16_t cbaud; //Centibaud (cbaud * 10 = kbaud)
							 | 
						||
| 
								 | 
							
								  void (*process_obd)(const struct elm_protocol*, const char*, uint16_t);
							 | 
						||
| 
								 | 
							
								  //init is used to init and de-init a protocol. Init functions should
							 | 
						||
| 
								 | 
							
								  //not do things that would leave a new protocol in an invalid state
							 | 
						||
| 
								 | 
							
								  //after the new protocol's init is called (e.g. No arming timers).
							 | 
						||
| 
								 | 
							
								  void (*init)(const struct elm_protocol*);
							 | 
						||
| 
								 | 
							
								  char* name;
							 | 
						||
| 
								 | 
							
								} elm_protocol_t;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const elm_protocol_t* ICACHE_FLASH_ATTR elm_current_proto();
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_reset_aux_timer();
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_autodetect_cb(bool);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const elm_protocol_t elm_protocols[];
							 | 
						||
| 
								 | 
							
								//(sizeof(elm_protocols)/sizeof(elm_protocol_t))
							 | 
						||
| 
								 | 
							
								#define ELM_PROTOCOL_COUNT 13
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define LOOPCOUNT_FULL 4
							 | 
						||
| 
								 | 
							
								static int loopcount = 0;
							 | 
						||
| 
								 | 
							
								static volatile os_timer_t elm_timeout;
							 | 
						||
| 
								 | 
							
								static volatile os_timer_t elm_proto_aux_timeout;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static bool is_auto_detecting = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Used only by elm_timer_cb, so not volatile
							 | 
						||
| 
								 | 
							
								static bool did_multimessage = false;
							 | 
						||
| 
								 | 
							
								static bool got_msg_this_run = false;
							 | 
						||
| 
								 | 
							
								static bool can_tx_worked = false;
							 | 
						||
| 
								 | 
							
								static uint8_t elm_msg_mode_ret_filter;
							 | 
						||
| 
								 | 
							
								static uint8_t elm_msg_pid_ret_filter;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*****************************************************
							 | 
						||
| 
								 | 
							
								 *** ELM protocol specification and implementation ***
							 | 
						||
| 
								 | 
							
								 ***  -> SAE J1850 implementation (Unsupported)    ***
							 | 
						||
| 
								 | 
							
								 *****************************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_process_obd_cmd_J1850(const elm_protocol_t* proto,
							 | 
						||
| 
								 | 
							
								                                                        const char *cmd, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  elm_append_rsp_const("NO DATA\r\r>");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*****************************************************
							 | 
						||
| 
								 | 
							
								 *** ELM protocol specification and implementation ***
							 | 
						||
| 
								 | 
							
								 ***  -> ISO 14230-4 implementation                ***
							 | 
						||
| 
								 | 
							
								 *****************************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const char *lin_cmd_backup = NULL; //Holds msg while bus init is done
							 | 
						||
| 
								 | 
							
								uint16_t lin_cmd_backup_len = 0;
							 | 
						||
| 
								 | 
							
								bool lin_waiting_keepalive_echo = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_process_obd_cmd_LIN5baud(const elm_protocol_t* proto,
							 | 
						||
| 
								 | 
							
								                                                           const char *cmd, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  elm_append_rsp_const("BUS INIT: ...ERROR\r\r>");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool ICACHE_FLASH_ATTR elm_lin_keepalive_echo() {
							 | 
						||
| 
								 | 
							
								  if(lin_waiting_keepalive_echo) {
							 | 
						||
| 
								 | 
							
								    for(int pass = 0; pass < 4 && lin_ringbuff_len < 5; pass++) {
							 | 
						||
| 
								 | 
							
								      elm_LIN_read_into_ringbuff();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    lin_waiting_keepalive_echo = false;
							 | 
						||
| 
								 | 
							
								    //keepalive Echo should always come before other message echo.
							 | 
						||
| 
								 | 
							
								    if(lin_ringbuff_len >= 5 && !elm_LIN_ringbuff_memcmp("\xc1\x33\xf1\x3e\x23", 5)){
							 | 
						||
| 
								 | 
							
								      lin_ringbuff_consume(5);
							 | 
						||
| 
								 | 
							
								      return true;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      os_printf("Keep alive echo failed\n");
							 | 
						||
| 
								 | 
							
								      return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_LINFast_keepalive_timer_cb(void *arg) {
							 | 
						||
| 
								 | 
							
								  if(!lin_bus_initialized) {
							 | 
						||
| 
								 | 
							
								    os_printf("WARNING! Elm LIN keepalive timer running while bus is not initialized\n");
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if(loopcount) {
							 | 
						||
| 
								 | 
							
								    os_printf("WARNING! Elm LIN keepalive timer during a tx/rx loop!\n");
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if(lin_ringbuff_len) {
							 | 
						||
| 
								 | 
							
								    os_printf("WARNING! lin_ringbuff_len should be 0 when a keepalive echo is processed.\n");
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(!elm_lin_keepalive_echo()) {
							 | 
						||
| 
								 | 
							
								    lin_bus_initialized = false;
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  elm_lin_obd_msg msg = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  msg.priority = 0xC0 | 1;
							 | 
						||
| 
								 | 
							
								  msg.receiver = 0x33;
							 | 
						||
| 
								 | 
							
								  msg.sender = 0xF1;
							 | 
						||
| 
								 | 
							
								  msg.dat[0] = 0x3E;
							 | 
						||
| 
								 | 
							
								  msg.dat[1] = msg.dat[0] + msg.priority + msg.receiver + msg.sender; // checksum
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  os_printf("Sending LIN KEEPALIVE: Priority: %02x; RecvAddr: %02x; SendAddr: %02x;  (%02x); ",
							 | 
						||
| 
								 | 
							
								            msg.priority, msg.receiver, msg.sender, 1);
							 | 
						||
| 
								 | 
							
								  for(int i = 0; i < 2; i++) os_printf("%02x ", msg.dat[i]);
							 | 
						||
| 
								 | 
							
								  os_printf("\n");
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  lin_waiting_keepalive_echo = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  panda_usbemu_kline_write(&msg);
							 | 
						||
| 
								 | 
							
								  elm_reset_aux_timer();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_init_LINFast(const elm_protocol_t* proto){
							 | 
						||
| 
								 | 
							
								  os_timer_disarm(&elm_proto_aux_timeout);
							 | 
						||
| 
								 | 
							
								  os_timer_setfn(&elm_proto_aux_timeout, (os_timer_func_t *)elm_LINFast_keepalive_timer_cb, proto);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  lin_bus_initialized = false;
							 | 
						||
| 
								 | 
							
								  lin_await_msg_echo = false;
							 | 
						||
| 
								 | 
							
								  lin_waiting_keepalive_echo = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  lin_cmd_backup = NULL;
							 | 
						||
| 
								 | 
							
								  lin_cmd_backup_len = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  lin_ringbuff_clear();
							 | 
						||
| 
								 | 
							
								  panda_clear_lin_txrx();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int ICACHE_FLASH_ATTR elm_LINFast_process_echo() {
							 | 
						||
| 
								 | 
							
								  if(!elm_lin_keepalive_echo()) {
							 | 
						||
| 
								 | 
							
								    os_printf("Keepalive echo not detected.\n");
							 | 
						||
| 
								 | 
							
								    lin_ringbuff_clear();
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(!lin_await_msg_echo) {
							 | 
						||
| 
								 | 
							
								    os_printf("Echo abort. Nothing waiting echo\n");
							 | 
						||
| 
								 | 
							
								    return 1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for(int i = 0; i < 4; i++){
							 | 
						||
| 
								 | 
							
								    if(lin_ringbuff_len < lin_last_sent_msg_len) elm_LIN_read_into_ringbuff();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(lin_ringbuff_len >= lin_last_sent_msg_len){
							 | 
						||
| 
								 | 
							
								      #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								      os_printf("Got enough data %d\n", lin_last_sent_msg_len);
							 | 
						||
| 
								 | 
							
								      #endif
							 | 
						||
| 
								 | 
							
								      if(!elm_LIN_ringbuff_memcmp((uint8_t*)&lin_last_sent_msg, lin_last_sent_msg_len)) {
							 | 
						||
| 
								 | 
							
								        #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								        os_printf("LIN data was sent successfully.\n");
							 | 
						||
| 
								 | 
							
								        #endif
							 | 
						||
| 
								 | 
							
								        lin_ringbuff_consume(lin_last_sent_msg_len);
							 | 
						||
| 
								 | 
							
								        lin_await_msg_echo = false;
							 | 
						||
| 
								 | 
							
								        return 1;
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								        os_printf("Echo not correct.\n");
							 | 
						||
| 
								 | 
							
								        os_printf("    RB Data (%d %d %d): ", lin_ringbuff_start, lin_ringbuff_end, lin_ringbuff_len);
							 | 
						||
| 
								 | 
							
								        for(int i = 0; i < sizeof(lin_ringbuff); i++)
							 | 
						||
| 
								 | 
							
								          os_printf("%02x ", lin_ringbuff[i]);
							 | 
						||
| 
								 | 
							
								        os_printf("\n");
							 | 
						||
| 
								 | 
							
								        os_printf("         MSG Data (%d): ", lin_last_sent_msg_len);
							 | 
						||
| 
								 | 
							
								        for(int i = 0; i < lin_last_sent_msg_len; i++)
							 | 
						||
| 
								 | 
							
								          os_printf("%02x ", ((uint8_t*)&lin_last_sent_msg)[i]);
							 | 
						||
| 
								 | 
							
								        os_printf("\n");
							 | 
						||
| 
								 | 
							
								        #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if(lin_bus_initialized || loopcount == 0 && i == 4) {
							 | 
						||
| 
								 | 
							
								          lin_ringbuff_clear();
							 | 
						||
| 
								 | 
							
								          return -1;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          os_printf("Lin init echo misaligned? Consuming byte (%02x). Retry.\n", lin_ringbuff_get(0));
							 | 
						||
| 
								 | 
							
								          lin_ringbuff_consume(1);
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return !lin_await_msg_echo; //true if echo handled
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_LINFast_timer_cb(void *arg){
							 | 
						||
| 
								 | 
							
								  const elm_protocol_t* proto = (const elm_protocol_t*) arg;
							 | 
						||
| 
								 | 
							
								  loopcount--;
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  os_printf("LIN CB call\n");
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(!lin_bus_initialized) {
							 | 
						||
| 
								 | 
							
								    os_printf("WARNING: LIN CB called without bus initialized!");
							 | 
						||
| 
								 | 
							
								    return; // TODO: shoulnd't ever happen. Handle?
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  int echo_result = elm_LINFast_process_echo();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(echo_result == -1 || (echo_result == 0 && loopcount == 0)) {
							 | 
						||
| 
								 | 
							
								    if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("BUS ERROR\r\r>");
							 | 
						||
| 
								 | 
							
								      elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    loopcount = 0;
							 | 
						||
| 
								 | 
							
								    lin_bus_initialized = false;
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(echo_result == 0) {
							 | 
						||
| 
								 | 
							
								    #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								    os_printf("Not ready to process\n");
							 | 
						||
| 
								 | 
							
								    #endif
							 | 
						||
| 
								 | 
							
								    os_timer_arm(&elm_timeout, 30, 0);
							 | 
						||
| 
								 | 
							
								    return; // Not ready to go on
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  os_printf("Processing ELM %d\n", lin_ringbuff_len);
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(loopcount>0) {
							 | 
						||
| 
								 | 
							
								    for(int pass = 0; pass < 16 && loopcount; pass++){
							 | 
						||
| 
								 | 
							
								      elm_LIN_read_into_ringbuff();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      while(lin_ringbuff_len > 0){
							 | 
						||
| 
								 | 
							
								        //if(lin_ringbuff_len > 0){
							 | 
						||
| 
								 | 
							
								        if(lin_ringbuff_get(0) & 0x80 != 0x80){
							 | 
						||
| 
								 | 
							
								          os_printf("Resetting LIN bus due to bad first byte.\n");
							 | 
						||
| 
								 | 
							
								          loopcount = 0;
							 | 
						||
| 
								 | 
							
								          lin_bus_initialized = false;
							 | 
						||
| 
								 | 
							
								          lin_ringbuff_clear();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								            elm_append_rsp_const("ERROR\r\r>");
							 | 
						||
| 
								 | 
							
								            elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        uint8_t newmsg_len = 4 + (lin_ringbuff_get(0) & 0x7);
							 | 
						||
| 
								 | 
							
								        if(lin_ringbuff_len >= newmsg_len) {
							 | 
						||
| 
								 | 
							
								          #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								          os_printf("Processing LIN MSG. BuffLen %d; expect %d. Dat: ", lin_ringbuff_len, newmsg_len);
							 | 
						||
| 
								 | 
							
								          for(int i = 0; i < newmsg_len; i++) os_printf("%02x ", lin_ringbuff_get(i));
							 | 
						||
| 
								 | 
							
								          os_printf("\n");
							 | 
						||
| 
								 | 
							
								          #endif
							 | 
						||
| 
								 | 
							
								          got_msg_this_run = true;
							 | 
						||
| 
								 | 
							
								          loopcount = LOOPCOUNT_FULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								            if(elm_mode_additional_headers){
							 | 
						||
| 
								 | 
							
								              for(int i = 0; i < newmsg_len; i++) elm_append_rsp_hex_byte(lin_ringbuff_get(i));
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								              for(int i = 3; i < newmsg_len - 1; i++) elm_append_rsp_hex_byte(lin_ringbuff_get(i));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            elm_append_rsp_const("\r");
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          lin_ringbuff_consume(newmsg_len);
							 | 
						||
| 
								 | 
							
								          //elm_reset_aux_timer();
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          break; //Stop consuming data if there is not enough data for the next msg.
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    os_timer_arm(&elm_timeout, 50, 0);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    bool got_msg_this_run_backup = got_msg_this_run;
							 | 
						||
| 
								 | 
							
								    if(!got_msg_this_run) {
							 | 
						||
| 
								 | 
							
								      #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								      os_printf("  No data collected\n");
							 | 
						||
| 
								 | 
							
								      #endif
							 | 
						||
| 
								 | 
							
								      if(!is_auto_detecting) {
							 | 
						||
| 
								 | 
							
								        elm_append_rsp_const("NO DATA\r");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    got_msg_this_run = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(!is_auto_detecting) {
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("\r>");
							 | 
						||
| 
								 | 
							
								      elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      elm_autodetect_cb(got_msg_this_run_backup);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //TX RX over, resume Keepalive timer
							 | 
						||
| 
								 | 
							
								    elm_reset_aux_timer();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_LINFast_businit_timer_cb(void *arg){
							 | 
						||
| 
								 | 
							
								  const elm_protocol_t* proto = (const elm_protocol_t*) arg;
							 | 
						||
| 
								 | 
							
								  loopcount--;
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  os_printf("LIN INIT CB call\n");
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  int echo_result = elm_LINFast_process_echo();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(echo_result == -1 || (echo_result == 0 && loopcount == 0)) {
							 | 
						||
| 
								 | 
							
								    #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								    os_printf("Init failed with echo test\n");
							 | 
						||
| 
								 | 
							
								    #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    loopcount = 0;
							 | 
						||
| 
								 | 
							
								    lin_bus_initialized = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								      if(echo_result == -1)
							 | 
						||
| 
								 | 
							
								        elm_append_rsp_const("BUS ERROR\r\r>");
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        elm_append_rsp_const("ERROR\r\r>");
							 | 
						||
| 
								 | 
							
								      elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      elm_autodetect_cb(false);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(echo_result == 0) {
							 | 
						||
| 
								 | 
							
								    #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								    os_printf("Not ready to process\n");
							 | 
						||
| 
								 | 
							
								    #endif
							 | 
						||
| 
								 | 
							
								    os_timer_arm(&elm_timeout, elm_mode_timeout, 0);
							 | 
						||
| 
								 | 
							
								    return; // Not ready to go on
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  os_printf("Bus init ready to process %d bytes\n", lin_ringbuff_len);
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(lin_bus_initialized) return; // TODO: shoulnd't ever happen. Handle?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(loopcount>0) {
							 | 
						||
| 
								 | 
							
								    //Keep waiting for response
							 | 
						||
| 
								 | 
							
								    for(int i = 0; i < 4; i++){
							 | 
						||
| 
								 | 
							
								      elm_LIN_read_into_ringbuff();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if(lin_ringbuff_len > 0){
							 | 
						||
| 
								 | 
							
								        if(lin_ringbuff_get(0) & 0x80 != 0x80){
							 | 
						||
| 
								 | 
							
								          os_printf("Resetting LIN bus due to bad first byte.\n");
							 | 
						||
| 
								 | 
							
								          loopcount = 0;
							 | 
						||
| 
								 | 
							
								          lin_ringbuff_clear();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								            elm_append_rsp_const("ERROR\r\r>");
							 | 
						||
| 
								 | 
							
								            elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            elm_autodetect_cb(false);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        uint8_t newmsg_len = 4 + (lin_ringbuff_get(0) & 0x7);
							 | 
						||
| 
								 | 
							
								        if(lin_ringbuff_len < newmsg_len) {
							 | 
						||
| 
								 | 
							
								          os_printf("Resetting LIN because returned init data was wrong.\n");
							 | 
						||
| 
								 | 
							
								          loopcount = 0;
							 | 
						||
| 
								 | 
							
								          lin_ringbuff_clear();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								            elm_append_rsp_const("ERROR\r\r>");
							 | 
						||
| 
								 | 
							
								            elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            elm_autodetect_cb(false);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if(!elm_LIN_ringbuff_memcmp("\x83\xF1\x10\xC1\x8F\xE9\xBD", 7)) {
							 | 
						||
| 
								 | 
							
								          lin_ringbuff_consume(7);
							 | 
						||
| 
								 | 
							
								          lin_bus_initialized = true;
							 | 
						||
| 
								 | 
							
								          //lin_ringbuff_clear();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          os_printf("BUS INITIALIZED\n");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          elm_reset_aux_timer();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if(!is_auto_detecting) {
							 | 
						||
| 
								 | 
							
								            elm_append_rsp_const("OK\r");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            //Do the send that was delayed
							 | 
						||
| 
								 | 
							
								            if(lin_cmd_backup_len) {
							 | 
						||
| 
								 | 
							
								              elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								              proto->process_obd(proto, lin_cmd_backup, lin_cmd_backup_len);
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								              elm_append_rsp_const("\r>");
							 | 
						||
| 
								 | 
							
								              elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								            os_printf("LIN success. Silent because in autodetect.\n");
							 | 
						||
| 
								 | 
							
								            #endif
							 | 
						||
| 
								 | 
							
								            elm_autodetect_cb(true);
							 | 
						||
| 
								 | 
							
								            // TODO: Since bus init is good, is it ok to skip sending the '0100' msg?
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    os_timer_arm(&elm_timeout, elm_mode_timeout, 0);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								    os_printf("Fall through on bus init\n");
							 | 
						||
| 
								 | 
							
								    #endif
							 | 
						||
| 
								 | 
							
								    if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("ERROR\r\r>");
							 | 
						||
| 
								 | 
							
								      elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      elm_autodetect_cb(false);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    elm_reset_aux_timer();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_process_obd_cmd_LINFast(const elm_protocol_t* proto,
							 | 
						||
| 
								 | 
							
								                                                          const char *cmd, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  elm_lin_obd_msg msg = {};
							 | 
						||
| 
								 | 
							
								  uint8_t bytelen = (len-1)/2;
							 | 
						||
| 
								 | 
							
								  if((bytelen > 7 && !elm_mode_allow_long) || bytelen > 8) {
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const("?\r\r>");
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  os_timer_disarm(&elm_proto_aux_timeout);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(!lin_bus_initialized) {
							 | 
						||
| 
								 | 
							
								    panda_clear_lin_txrx();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(!is_auto_detecting)
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("BUS INIT: ");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    lin_cmd_backup = cmd;
							 | 
						||
| 
								 | 
							
								    lin_cmd_backup_len = len;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    bytelen = 1;
							 | 
						||
| 
								 | 
							
								    msg.dat[0] = 0x81;
							 | 
						||
| 
								 | 
							
								    msg.dat[1] = 0x81; // checksum
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    panda_kline_wakeup_pulse();
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    bytelen = min(bytelen, 7);
							 | 
						||
| 
								 | 
							
								    for(int i = 0; i < bytelen; i++){
							 | 
						||
| 
								 | 
							
								      msg.dat[i] = elm_decode_hex_byte(&cmd[i*2]);
							 | 
						||
| 
								 | 
							
								      msg.dat[bytelen] += msg.dat[i];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    elm_msg_mode_ret_filter = msg.dat[0];
							 | 
						||
| 
								 | 
							
								    elm_msg_pid_ret_filter = msg.dat[1];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  msg.priority = 0xC0 | bytelen;
							 | 
						||
| 
								 | 
							
								  msg.receiver = 0x33;
							 | 
						||
| 
								 | 
							
								  msg.sender = 0xF1;
							 | 
						||
| 
								 | 
							
								  msg.dat[bytelen] += msg.priority + msg.receiver + msg.sender; // checksum
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  os_printf("Sending LIN OBD: Priority: %02x; RecvAddr: %02x; SendAddr: %02x;  (%02x); ",
							 | 
						||
| 
								 | 
							
								            msg.priority, msg.receiver, msg.sender, bytelen);
							 | 
						||
| 
								 | 
							
								  for(int i = 0; i < 8; i++) os_printf("%02x ", msg.dat[i]);
							 | 
						||
| 
								 | 
							
								  os_printf("\n");
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  lin_last_sent_msg_len = (msg.priority & 0x07) + 4;
							 | 
						||
| 
								 | 
							
								  memcpy(&lin_last_sent_msg, &msg, lin_last_sent_msg_len);
							 | 
						||
| 
								 | 
							
								  lin_await_msg_echo = true;
							 | 
						||
| 
								 | 
							
								  panda_usbemu_kline_write(&msg);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  loopcount = LOOPCOUNT_FULL + 1;
							 | 
						||
| 
								 | 
							
								  os_timer_disarm(&elm_timeout);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(lin_bus_initialized) {
							 | 
						||
| 
								 | 
							
								    os_timer_setfn(&elm_timeout, (os_timer_func_t *)elm_LINFast_timer_cb, proto);
							 | 
						||
| 
								 | 
							
								    elm_LINFast_timer_cb((void*)proto);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    os_timer_setfn(&elm_timeout, (os_timer_func_t *)elm_LINFast_businit_timer_cb, proto);
							 | 
						||
| 
								 | 
							
								    elm_LINFast_businit_timer_cb((void*)proto);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*****************************************************
							 | 
						||
| 
								 | 
							
								 *** ELM protocol specification and implementation ***
							 | 
						||
| 
								 | 
							
								 ***  -> ISO 15765-4 implementation                ***
							 | 
						||
| 
								 | 
							
								 *****************************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_ISO15765_timer_cb(void *arg){
							 | 
						||
| 
								 | 
							
								  const elm_protocol_t* proto = (const elm_protocol_t*) arg;
							 | 
						||
| 
								 | 
							
								  loopcount--;
							 | 
						||
| 
								 | 
							
								  if(loopcount>0) {
							 | 
						||
| 
								 | 
							
								    for(int pass = 0; pass < 16 && loopcount; pass++){
							 | 
						||
| 
								 | 
							
								      panda_can_msg_t *can_msgs;
							 | 
						||
| 
								 | 
							
								      int num_can_msgs = panda_usbemu_can_read(&can_msgs);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								      if(num_can_msgs) os_printf("  Received %d can messages\n", num_can_msgs);
							 | 
						||
| 
								 | 
							
								      #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if(num_can_msgs < 0) continue;
							 | 
						||
| 
								 | 
							
								      if(!num_can_msgs) break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      for(int i = 0; i < num_can_msgs; i++){
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        panda_can_msg_t *recv = &can_msgs[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								        os_printf("    RECV: Bus: %d; Addr: %08x; ext: %d; tx: %d; Len: %d; ",
							 | 
						||
| 
								 | 
							
								                  recv->bus, panda_get_can_addr(recv), recv->ext, recv->tx, recv->len);
							 | 
						||
| 
								 | 
							
								        for(int j = 0; j < recv->len; j++) os_printf("%02x ", recv->data[j]);
							 | 
						||
| 
								 | 
							
								        os_printf("Ts: %d\n", recv->ts);
							 | 
						||
| 
								 | 
							
								        #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (recv->bus==0 && recv->len == 8 &&
							 | 
						||
| 
								 | 
							
								            (
							 | 
						||
| 
								 | 
							
								             (proto->type == CAN11 && !recv->ext && (panda_get_can_addr(recv) & 0x7F8) == 0x7E8) ||
							 | 
						||
| 
								 | 
							
								             (proto->type == CAN29 && recv->ext && (panda_get_can_addr(recv) & 0x1FFFFF00) == 0x18DAF100)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								           ) {
							 | 
						||
| 
								 | 
							
								          if(recv->data[0] <= 7 &&
							 | 
						||
| 
								 | 
							
								             recv->data[1] == (0x40|elm_msg_mode_ret_filter) &&
							 | 
						||
| 
								 | 
							
								             recv->data[2] == elm_msg_pid_ret_filter) {
							 | 
						||
| 
								 | 
							
								            got_msg_this_run = true;
							 | 
						||
| 
								 | 
							
								            loopcount = LOOPCOUNT_FULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								            os_printf("      CAN msg response, index: %d\n", i);
							 | 
						||
| 
								 | 
							
								            #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								              if(elm_mode_additional_headers){
							 | 
						||
| 
								 | 
							
								                elm_append_rsp_can_msg_addr(recv);
							 | 
						||
| 
								 | 
							
								                for(int j = 0; j < recv->data[0]+1; j++) elm_append_rsp_hex_byte(recv->data[j]);
							 | 
						||
| 
								 | 
							
								              } else {
							 | 
						||
| 
								 | 
							
								                for(int j = 1; j < recv->data[0]+1; j++) elm_append_rsp_hex_byte(recv->data[j]);
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              elm_append_rsp_const("\r");
							 | 
						||
| 
								 | 
							
								              elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          } else if((recv->data[0] & 0xF0) == 0x10 &&
							 | 
						||
| 
								 | 
							
								                    recv->data[2] == (0x40|elm_msg_mode_ret_filter) &&
							 | 
						||
| 
								 | 
							
								                    recv->data[3] == elm_msg_pid_ret_filter) {
							 | 
						||
| 
								 | 
							
								            got_msg_this_run = true;
							 | 
						||
| 
								 | 
							
								            loopcount = LOOPCOUNT_FULL;
							 | 
						||
| 
								 | 
							
								            panda_usbemu_can_write(0,
							 | 
						||
| 
								 | 
							
								                                   (proto->type==CAN11) ?
							 | 
						||
| 
								 | 
							
								                                   0x7E0 | (panda_get_can_addr(recv)&0x7) :
							 | 
						||
| 
								 | 
							
								                                   (0x18DA00F1 | (((panda_get_can_addr(recv))&0xFF)<<8)),
							 | 
						||
| 
								 | 
							
								                                   "\x30\x00\x00", 3);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            did_multimessage = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								            os_printf("      CAN multimsg start response, index: %d, len %d\n", i,
							 | 
						||
| 
								 | 
							
								                      ((recv->data[0]&0xF)<<8) | recv->data[1]);
							 | 
						||
| 
								 | 
							
								            #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								              if(!elm_mode_additional_headers) {
							 | 
						||
| 
								 | 
							
								                elm_append_rsp(&hex_lookup[recv->data[0]&0xF], 1);
							 | 
						||
| 
								 | 
							
								                elm_append_rsp_hex_byte(recv->data[1]);
							 | 
						||
| 
								 | 
							
								                elm_append_rsp_const("\r0:");
							 | 
						||
| 
								 | 
							
								                if(elm_mode_print_spaces) elm_append_rsp_const(" ");
							 | 
						||
| 
								 | 
							
								                for(int j = 2; j < 8; j++) elm_append_rsp_hex_byte(recv->data[j]);
							 | 
						||
| 
								 | 
							
								              } else {
							 | 
						||
| 
								 | 
							
								                elm_append_rsp_can_msg_addr(recv);
							 | 
						||
| 
								 | 
							
								                for(int j = 0; j < 8; j++) elm_append_rsp_hex_byte(recv->data[j]);
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              elm_append_rsp_const("\r");
							 | 
						||
| 
								 | 
							
								              elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          } else if (did_multimessage && (recv->data[0] & 0xF0) == 0x20) {
							 | 
						||
| 
								 | 
							
								            got_msg_this_run = true;
							 | 
						||
| 
								 | 
							
								            loopcount = LOOPCOUNT_FULL;
							 | 
						||
| 
								 | 
							
								            #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								            os_printf("      CAN multimsg data response, index: %d\n", i);
							 | 
						||
| 
								 | 
							
								            #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if(!is_auto_detecting){
							 | 
						||
| 
								 | 
							
								              if(!elm_mode_additional_headers) {
							 | 
						||
| 
								 | 
							
								                elm_append_rsp(&hex_lookup[recv->data[0] & 0xF], 1);
							 | 
						||
| 
								 | 
							
								                elm_append_rsp_const(":");
							 | 
						||
| 
								 | 
							
								                if(elm_mode_print_spaces) elm_append_rsp_const(" ");
							 | 
						||
| 
								 | 
							
								                for(int j = 1; j < 8; j++) elm_append_rsp_hex_byte(recv->data[j]);
							 | 
						||
| 
								 | 
							
								              } else {
							 | 
						||
| 
								 | 
							
								                elm_append_rsp_can_msg_addr(recv);
							 | 
						||
| 
								 | 
							
								                for(int j = 0; j < 8; j++) elm_append_rsp_hex_byte(recv->data[j]);
							 | 
						||
| 
								 | 
							
								              }
							 | 
						||
| 
								 | 
							
								              elm_append_rsp_const("\r");
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        } else if (recv->bus == 0x80 && recv->len == 8 &&
							 | 
						||
| 
								 | 
							
								                   (panda_get_can_addr(recv) == ((proto->type==CAN11) ? 0x7DF : 0x18DB33F1))
							 | 
						||
| 
								 | 
							
								                  ) {
							 | 
						||
| 
								 | 
							
								          //Can send receipt
							 | 
						||
| 
								 | 
							
								          #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								          os_printf("      Got CAN tx receipt\n");
							 | 
						||
| 
								 | 
							
								          #endif
							 | 
						||
| 
								 | 
							
								          can_tx_worked = true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    os_timer_arm(&elm_timeout, elm_mode_timeout, 0);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    bool got_msg_this_run_backup = got_msg_this_run;
							 | 
						||
| 
								 | 
							
								    if(did_multimessage) {
							 | 
						||
| 
								 | 
							
								      os_printf("  End of multi message\n");
							 | 
						||
| 
								 | 
							
								    } else if(!got_msg_this_run) {
							 | 
						||
| 
								 | 
							
								      os_printf("  No data collected\n");
							 | 
						||
| 
								 | 
							
								      if(!is_auto_detecting) {
							 | 
						||
| 
								 | 
							
								        if(can_tx_worked) {
							 | 
						||
| 
								 | 
							
								          elm_append_rsp_const("NO DATA\r");
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          elm_append_rsp_const("CAN ERROR\r");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    did_multimessage = false;
							 | 
						||
| 
								 | 
							
								    got_msg_this_run = false;
							 | 
						||
| 
								 | 
							
								    can_tx_worked = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(!is_auto_detecting) {
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("\r>");
							 | 
						||
| 
								 | 
							
								      elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      elm_autodetect_cb(got_msg_this_run_backup);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_init_ISO15765(const elm_protocol_t* proto){
							 | 
						||
| 
								 | 
							
								  panda_set_can0_cbaud(proto->cbaud);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_process_obd_cmd_ISO15765(const elm_protocol_t* proto,
							 | 
						||
| 
								 | 
							
								                                                           const char *cmd, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  elm_can_obd_msg msg = {};
							 | 
						||
| 
								 | 
							
								  msg.len = (len-1)/2;
							 | 
						||
| 
								 | 
							
								  if((msg.len > 7 && !elm_mode_allow_long) || msg.len > 8) {
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const("?\r\r>");
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  msg.len = min(msg.len, 7);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for(int i = 0; i < msg.len; i++)
							 | 
						||
| 
								 | 
							
								    msg.dat[i] = elm_decode_hex_byte(&cmd[i*2]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  elm_msg_mode_ret_filter = msg.dat[0];
							 | 
						||
| 
								 | 
							
								  elm_msg_pid_ret_filter = msg.dat[1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  os_printf("Sending CAN OBD: %02x; ", msg.len);
							 | 
						||
| 
								 | 
							
								  for(int i = 0; i < 7; i++)
							 | 
						||
| 
								 | 
							
								    os_printf("%02x ", msg.dat[i]);
							 | 
						||
| 
								 | 
							
								  os_printf("\n");
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  panda_clear_can_rx();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  panda_usbemu_can_write(0, (proto->type==CAN11) ? 0x7DF : 0x18DB33F1,
							 | 
						||
| 
								 | 
							
								                         (uint8_t*)&msg, msg.len+1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  os_printf("Starting up timer\n");
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  loopcount = LOOPCOUNT_FULL;
							 | 
						||
| 
								 | 
							
								  os_timer_disarm(&elm_timeout);
							 | 
						||
| 
								 | 
							
								  os_timer_setfn(&elm_timeout, (os_timer_func_t *)elm_ISO15765_timer_cb, proto);
							 | 
						||
| 
								 | 
							
								  os_timer_arm(&elm_timeout, elm_mode_timeout, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*****************************************************
							 | 
						||
| 
								 | 
							
								 *** ELM protocol specification and implementation ***
							 | 
						||
| 
								 | 
							
								 ***  -> Stuf for unsupported CAN protocols        ***
							 | 
						||
| 
								 | 
							
								 *****************************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_process_obd_cmd_CANGen(const elm_protocol_t* proto,
							 | 
						||
| 
								 | 
							
								                                                         const char *cmd, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  elm_append_rsp_const("NO DATA\r\r>");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*****************************************************
							 | 
						||
| 
								 | 
							
								 *** ELM protocol specification and implementation ***
							 | 
						||
| 
								 | 
							
								 ***  -> AUTO Detect implementation                ***
							 | 
						||
| 
								 | 
							
								 *****************************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int elm_autodetect_proto_iter;
							 | 
						||
| 
								 | 
							
								static uint16_t elm_staged_auto_msg_len;
							 | 
						||
| 
								 | 
							
								static const char* elm_staged_auto_msg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_autodetect_cb(bool proto_worked){
							 | 
						||
| 
								 | 
							
								  if(proto_worked) {
							 | 
						||
| 
								 | 
							
								    os_printf("Autodetect proto success\n");
							 | 
						||
| 
								 | 
							
								    is_auto_detecting = false;
							 | 
						||
| 
								 | 
							
								    elm_selected_protocol = elm_autodetect_proto_iter;
							 | 
						||
| 
								 | 
							
								    elm_current_proto()->process_obd(elm_current_proto(),
							 | 
						||
| 
								 | 
							
								                                     elm_staged_auto_msg, elm_staged_auto_msg_len);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    for(elm_autodetect_proto_iter++; elm_autodetect_proto_iter < ELM_PROTOCOL_COUNT;
							 | 
						||
| 
								 | 
							
								        elm_autodetect_proto_iter++){
							 | 
						||
| 
								 | 
							
								      const elm_protocol_t *proto = &elm_protocols[elm_autodetect_proto_iter];
							 | 
						||
| 
								 | 
							
								      if(proto->supported && proto->type != AUTO) {
							 | 
						||
| 
								 | 
							
								        os_printf("*** AUTO trying '%s'\n", proto->name);
							 | 
						||
| 
								 | 
							
								        proto->init(proto);
							 | 
						||
| 
								 | 
							
								        proto->process_obd(proto, "0100\r", 5); // Try sending on the bus
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //if(elm_autodetect_main()) return;
							 | 
						||
| 
								 | 
							
								    is_auto_detecting = false;
							 | 
						||
| 
								 | 
							
								    os_printf("Autodetect failed\n");
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const("UNABLE TO CONNECT\r\r>");
							 | 
						||
| 
								 | 
							
								    elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_process_obd_cmd_AUTO(const elm_protocol_t* proto,
							 | 
						||
| 
								 | 
							
								                                                       const char *cmd, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  elm_append_rsp_const("SEARCHING...\r");
							 | 
						||
| 
								 | 
							
								  elm_staged_auto_msg_len = len;
							 | 
						||
| 
								 | 
							
								  elm_staged_auto_msg = cmd;
							 | 
						||
| 
								 | 
							
								  is_auto_detecting = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  elm_autodetect_proto_iter = 0;
							 | 
						||
| 
								 | 
							
								  elm_autodetect_cb(false);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*****************************************************
							 | 
						||
| 
								 | 
							
								 *** ELM protocol specification and implementation ***
							 | 
						||
| 
								 | 
							
								 ***  -> Protocol Registry and related functions.  ***
							 | 
						||
| 
								 | 
							
								 *****************************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const elm_protocol_t elm_protocols[] = {
							 | 
						||
| 
								 | 
							
								  {true,  AUTO,  0,    elm_process_obd_cmd_AUTO,     NULL,              "AUTO",                    },
							 | 
						||
| 
								 | 
							
								  {false, NA,    416,  elm_process_obd_cmd_J1850,    NULL,              "SAE J1850 PWM",           },
							 | 
						||
| 
								 | 
							
								  {false, NA,    104,  elm_process_obd_cmd_J1850,    NULL,              "SAE J1850 VPW",           },
							 | 
						||
| 
								 | 
							
								  {false, LIN,   104,  elm_process_obd_cmd_LIN5baud, NULL,              "ISO 9141-2",              },
							 | 
						||
| 
								 | 
							
								  {false, LIN,   104,  elm_process_obd_cmd_LIN5baud, NULL,              "ISO 14230-4 (KWP 5BAUD)", },
							 | 
						||
| 
								 | 
							
								  {true,  LIN,   104,  elm_process_obd_cmd_LINFast,  elm_init_LINFast,  "ISO 14230-4 (KWP FAST)",  },
							 | 
						||
| 
								 | 
							
								  {true,  CAN11, 5000, elm_process_obd_cmd_ISO15765, elm_init_ISO15765, "ISO 15765-4 (CAN 11/500)",},
							 | 
						||
| 
								 | 
							
								  {true,  CAN29, 5000, elm_process_obd_cmd_ISO15765, elm_init_ISO15765, "ISO 15765-4 (CAN 29/500)",},
							 | 
						||
| 
								 | 
							
								  {true,  CAN11, 2500, elm_process_obd_cmd_ISO15765, elm_init_ISO15765, "ISO 15765-4 (CAN 11/250)",},
							 | 
						||
| 
								 | 
							
								  {true,  CAN29, 2500, elm_process_obd_cmd_ISO15765, elm_init_ISO15765, "ISO 15765-4 (CAN 29/250)",},
							 | 
						||
| 
								 | 
							
								  {false, CAN29, 2500, elm_process_obd_cmd_CANGen,   NULL,              "SAE J1939 (CAN 29/250)",  },
							 | 
						||
| 
								 | 
							
								  {false, CAN11, 1250, elm_process_obd_cmd_CANGen,   NULL,              "USER1 (CAN 11/125)",      },
							 | 
						||
| 
								 | 
							
								  {false, CAN11, 500,  elm_process_obd_cmd_CANGen,   NULL,              "USER2 (CAN 11/50)",       },
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const elm_protocol_t* ICACHE_FLASH_ATTR elm_current_proto() {
							 | 
						||
| 
								 | 
							
								  return &elm_protocols[elm_selected_protocol];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_reset_aux_timer() {
							 | 
						||
| 
								 | 
							
								    os_timer_disarm(&elm_proto_aux_timeout);
							 | 
						||
| 
								 | 
							
								    if(elm_mode_keepalive_period)
							 | 
						||
| 
								 | 
							
								      os_timer_arm(&elm_proto_aux_timeout, elm_mode_keepalive_period, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_proto_reinit(const elm_protocol_t *proto) {
							 | 
						||
| 
								 | 
							
								    if(proto->init) proto->init(proto);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*******************************************
							 | 
						||
| 
								 | 
							
								 *** ELM AT command parsing and handling ***
							 | 
						||
| 
								 | 
							
								 *******************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum at_cmd_ids_t { // FULL ELM 1.0 list
							 | 
						||
| 
								 | 
							
								  AT_INVALID, //Fake
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  AT_AMP1,
							 | 
						||
| 
								 | 
							
								  AT_AL,
							 | 
						||
| 
								 | 
							
								  AT_AT0, AT_AT1, AT_AT2, // Added ELM 1.2, expected by Torque
							 | 
						||
| 
								 | 
							
								  AT_BD,
							 | 
						||
| 
								 | 
							
								  AT_BI,
							 | 
						||
| 
								 | 
							
								  AT_CAF0, AT_CAF1,
							 | 
						||
| 
								 | 
							
								  AT_CF_8, AT_CF_3,
							 | 
						||
| 
								 | 
							
								  AT_CFC0, AT_CFC1,
							 | 
						||
| 
								 | 
							
								  AT_CM_8, AT_CM_3,
							 | 
						||
| 
								 | 
							
								  AT_CP,
							 | 
						||
| 
								 | 
							
								  AT_CS,
							 | 
						||
| 
								 | 
							
								  AT_CV,
							 | 
						||
| 
								 | 
							
								  AT_D,
							 | 
						||
| 
								 | 
							
								  AT_DP, AT_DPN,
							 | 
						||
| 
								 | 
							
								  AT_E0, AT_E1,
							 | 
						||
| 
								 | 
							
								  AT_H0, AT_H1,
							 | 
						||
| 
								 | 
							
								  AT_I,
							 | 
						||
| 
								 | 
							
								  AT_IB10,
							 | 
						||
| 
								 | 
							
								  AT_IB96,
							 | 
						||
| 
								 | 
							
								  AT_L0, AT_L1,
							 | 
						||
| 
								 | 
							
								  AT_M0, AT_M1, AT_MA,
							 | 
						||
| 
								 | 
							
								  AT_MR,
							 | 
						||
| 
								 | 
							
								  AT_MT,
							 | 
						||
| 
								 | 
							
								  AT_NL,
							 | 
						||
| 
								 | 
							
								  AT_PC,
							 | 
						||
| 
								 | 
							
								  AT_R0, AT_R1,
							 | 
						||
| 
								 | 
							
								  AT_RV,
							 | 
						||
| 
								 | 
							
								  AT_S0, AT_S1, // Added ELM 1.3, expected by Torque
							 | 
						||
| 
								 | 
							
								  AT_SH_6, AT_SH_3,
							 | 
						||
| 
								 | 
							
								  AT_SPA, AT_SP,
							 | 
						||
| 
								 | 
							
								  AT_ST,
							 | 
						||
| 
								 | 
							
								  AT_SW,
							 | 
						||
| 
								 | 
							
								  AT_TPA, AT_TP,
							 | 
						||
| 
								 | 
							
								  AT_WM_XYZA, AT_WM_XYZAB, AT_WM_XYZABC,
							 | 
						||
| 
								 | 
							
								  AT_WS,
							 | 
						||
| 
								 | 
							
								  AT_Z,
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct {
							 | 
						||
| 
								 | 
							
								  char* name;
							 | 
						||
| 
								 | 
							
								  uint8_t name_len;
							 | 
						||
| 
								 | 
							
								  uint8_t cmd_len;
							 | 
						||
| 
								 | 
							
								  enum at_cmd_ids_t id;
							 | 
						||
| 
								 | 
							
								} at_cmd_reg_t;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static const at_cmd_reg_t at_cmd_reg[] = {
							 | 
						||
| 
								 | 
							
								  {"@1",  2, 2, AT_AMP1},
							 | 
						||
| 
								 | 
							
								  {"AL",  2, 2, AT_AL},
							 | 
						||
| 
								 | 
							
								  {"AT0", 3, 3, AT_AT0}, // Added ELM 1.2, expected by Torque
							 | 
						||
| 
								 | 
							
								  {"AT1", 3, 3, AT_AT1}, // Added ELM 1.2, expected by Torque
							 | 
						||
| 
								 | 
							
								  {"AT2", 3, 3, AT_AT2}, // Added ELM 1.2, expected by Torque
							 | 
						||
| 
								 | 
							
								  {"DP",  2, 2, AT_DP},
							 | 
						||
| 
								 | 
							
								  {"DPN", 3, 3, AT_DPN},
							 | 
						||
| 
								 | 
							
								  {"E0",  2, 2, AT_E0},
							 | 
						||
| 
								 | 
							
								  {"E1",  2, 2, AT_E1},
							 | 
						||
| 
								 | 
							
								  {"H0",  2, 2, AT_H0},
							 | 
						||
| 
								 | 
							
								  {"H1",  2, 2, AT_H1},
							 | 
						||
| 
								 | 
							
								  {"I",   1, 1, AT_I},
							 | 
						||
| 
								 | 
							
								  {"L0",  2, 2, AT_L0},
							 | 
						||
| 
								 | 
							
								  {"L1",  2, 2, AT_L1},
							 | 
						||
| 
								 | 
							
								  {"M0",  2, 2, AT_M0},
							 | 
						||
| 
								 | 
							
								  //{"M1",  2, 2, AT_M1},
							 | 
						||
| 
								 | 
							
								  {"NL",  2, 2, AT_NL},
							 | 
						||
| 
								 | 
							
								  {"PC",  2, 2, AT_PC},
							 | 
						||
| 
								 | 
							
								  {"S0",  2, 2, AT_S0}, // Added ELM 1.3, expected by Torque
							 | 
						||
| 
								 | 
							
								  {"S1",  2, 2, AT_S1}, // Added ELM 1.3, expected by Torque
							 | 
						||
| 
								 | 
							
								  {"SP",  2, 3, AT_SP},
							 | 
						||
| 
								 | 
							
								  {"SPA", 3, 4, AT_SPA},
							 | 
						||
| 
								 | 
							
								  {"ST",  2, 4, AT_ST},
							 | 
						||
| 
								 | 
							
								  {"SW",  2, 4, AT_SW},
							 | 
						||
| 
								 | 
							
								  {"Z",   1, 1, AT_Z},
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								#define AT_CMD_REG_LEN (sizeof(at_cmd_reg)/sizeof(at_cmd_reg_t))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static enum at_cmd_ids_t ICACHE_FLASH_ATTR elm_parse_at_cmd(char *cmd, uint16_t len){
							 | 
						||
| 
								 | 
							
								  int i;
							 | 
						||
| 
								 | 
							
								  for(i=0; i<AT_CMD_REG_LEN; i++){
							 | 
						||
| 
								 | 
							
								    at_cmd_reg_t candidate = at_cmd_reg[i];
							 | 
						||
| 
								 | 
							
								    if(candidate.cmd_len == len-1 && !memcmp(cmd, candidate.name, candidate.name_len))
							 | 
						||
| 
								 | 
							
								      return candidate.id;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return AT_INVALID;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_process_at_cmd(char *cmd, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  uint8_t tmp;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  os_printf("AT COMMAND ");
							 | 
						||
| 
								 | 
							
								  for(int i = 0; i < len; i++) os_printf("%c", cmd[i]);
							 | 
						||
| 
								 | 
							
								  os_printf("\r\n");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch(elm_parse_at_cmd(cmd, len)){
							 | 
						||
| 
								 | 
							
								  case AT_AMP1: //RETURN DEVICE DESCRIPTION
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const(DEVICE_DESC);
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  case AT_AL: //DISABLE LONG MESSAGE SUPPORT (>7 BYTES)
							 | 
						||
| 
								 | 
							
								    elm_mode_allow_long = true;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_AT0: //DISABLE ADAPTIVE TIMING
							 | 
						||
| 
								 | 
							
								    elm_mode_adaptive_timing = 0;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_AT1: //SET ADAPTIVE TIMING TO AUTO1
							 | 
						||
| 
								 | 
							
								    elm_mode_adaptive_timing = 1;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_AT2: //SET ADAPTIVE TIMING TO AUTO2
							 | 
						||
| 
								 | 
							
								    elm_mode_adaptive_timing = 2;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_DP: //DESCRIBE THE PROTOCOL BY NAME
							 | 
						||
| 
								 | 
							
								    if(elm_mode_auto_protocol && elm_selected_protocol != 0)
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("AUTO, ");
							 | 
						||
| 
								 | 
							
								    elm_append_rsp(elm_current_proto()->name,
							 | 
						||
| 
								 | 
							
								                   strlen(elm_current_proto()->name));
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const("\r\r");
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  case AT_DPN: //DESCRIBE THE PROTOCOL BY NUMBER
							 | 
						||
| 
								 | 
							
								    //TODO: Required. Report currently selected protocol
							 | 
						||
| 
								 | 
							
								    if(elm_mode_auto_protocol)
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("A");
							 | 
						||
| 
								 | 
							
								    elm_append_rsp(&hex_lookup[elm_selected_protocol], 1);
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const("\r\r");
							 | 
						||
| 
								 | 
							
								    return; // Don't display 'OK'
							 | 
						||
| 
								 | 
							
								  case AT_E0: //ECHO OFF
							 | 
						||
| 
								 | 
							
								    elm_mode_echo = false;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_E1: //ECHO ON
							 | 
						||
| 
								 | 
							
								    elm_mode_echo = true;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_H0: //SHOW FULL CAN HEADERS OFF
							 | 
						||
| 
								 | 
							
								    elm_mode_additional_headers = false;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_H1: //SHOW FULL CAN HEADERS ON
							 | 
						||
| 
								 | 
							
								    elm_mode_additional_headers = true;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_I: //IDENTIFY SELF
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const(IDENT_MSG);
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  case AT_L0: //LINEFEED OFF
							 | 
						||
| 
								 | 
							
								    elm_mode_linefeed = false;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_L1: //LINEFEED ON
							 | 
						||
| 
								 | 
							
								    elm_mode_linefeed = true;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_M0: //DISABLE NONVOLATILE STORAGE
							 | 
						||
| 
								 | 
							
								    //Memory storage is likely unnecessary
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_NL: //DISABLE LONG MESSAGE SUPPORT (>7 BYTES)
							 | 
						||
| 
								 | 
							
								    elm_mode_allow_long = false;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_PC: //PROTOCOL CANCEL (Stop timers and stuff)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      //Init functions should idenpotently prepare the protocol to be used.
							 | 
						||
| 
								 | 
							
								      //Thus, the init function can be used as a protocol cancel function
							 | 
						||
| 
								 | 
							
								      elm_proto_reinit(elm_current_proto());
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  case AT_S0: //DISABLE PRINTING SPACES IN ECU RESPONSES
							 | 
						||
| 
								 | 
							
								    elm_mode_print_spaces = false;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_S1: //ENABLE PRINTING SPACES IN ECU RESPONSES
							 | 
						||
| 
								 | 
							
								    elm_mode_print_spaces = true;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_SP: //SET PROTOCOL
							 | 
						||
| 
								 | 
							
								    tmp = elm_decode_hex_char(cmd[2]);
							 | 
						||
| 
								 | 
							
								    if(tmp == -1 || tmp >= ELM_PROTOCOL_COUNT) {
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("?\r\r");
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //De-Init previous protocol
							 | 
						||
| 
								 | 
							
								    elm_proto_reinit(elm_current_proto());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    elm_selected_protocol = tmp;
							 | 
						||
| 
								 | 
							
								    elm_mode_auto_protocol = (tmp == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //Init new protocol
							 | 
						||
| 
								 | 
							
								    elm_proto_reinit(elm_current_proto());
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_SPA: //SET PROTOCOL WITH AUTO FALLBACK
							 | 
						||
| 
								 | 
							
								    tmp = elm_decode_hex_char(cmd[3]);
							 | 
						||
| 
								 | 
							
								    if(tmp == -1 || tmp >= ELM_PROTOCOL_COUNT) {
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("?\r\r");
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //De-Init previous protocol
							 | 
						||
| 
								 | 
							
								    elm_proto_reinit(elm_current_proto());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    elm_selected_protocol = tmp;
							 | 
						||
| 
								 | 
							
								    elm_mode_auto_protocol = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    //Init new protocol
							 | 
						||
| 
								 | 
							
								    elm_proto_reinit(elm_current_proto());
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_ST:  //SET TIMEOUT
							 | 
						||
| 
								 | 
							
								    if(!elm_check_valid_hex_chars(&cmd[2], 2)) {
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("?\r\r");
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    tmp = elm_decode_hex_byte(&cmd[2]);
							 | 
						||
| 
								 | 
							
								    //20 for CAN, 4 for LIN
							 | 
						||
| 
								 | 
							
								    elm_mode_timeout = tmp ? tmp*20 : ELM_MODE_TIMEOUT_DEFAULT;
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_SW:  //SET WAKEUP TIME INTERVAL
							 | 
						||
| 
								 | 
							
								    if(!elm_check_valid_hex_chars(&cmd[2], 2)) {
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("?\r\r");
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    tmp = elm_decode_hex_byte(&cmd[2]);
							 | 
						||
| 
								 | 
							
								    elm_mode_keepalive_period = tmp ? max(tmp, 0x20) * 20 : 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(lin_bus_initialized){
							 | 
						||
| 
								 | 
							
								      os_timer_disarm(&elm_proto_aux_timeout);
							 | 
						||
| 
								 | 
							
								      if(elm_mode_keepalive_period)
							 | 
						||
| 
								 | 
							
								        os_timer_arm(&elm_proto_aux_timeout, elm_mode_keepalive_period, 0);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  case AT_Z: //RESET
							 | 
						||
| 
								 | 
							
								    elm_mode_echo = true;
							 | 
						||
| 
								 | 
							
								    elm_mode_linefeed = false;
							 | 
						||
| 
								 | 
							
								    elm_mode_additional_headers = false;
							 | 
						||
| 
								 | 
							
								    elm_mode_auto_protocol = true;
							 | 
						||
| 
								 | 
							
								    elm_selected_protocol = ELM_MODE_SELECTED_PROTOCOL_DEFAULT;
							 | 
						||
| 
								 | 
							
								    elm_mode_print_spaces = true;
							 | 
						||
| 
								 | 
							
								    elm_mode_adaptive_timing = 1;
							 | 
						||
| 
								 | 
							
								    elm_mode_allow_long = false;
							 | 
						||
| 
								 | 
							
								    elm_mode_timeout = ELM_MODE_TIMEOUT_DEFAULT;
							 | 
						||
| 
								 | 
							
								    elm_mode_keepalive_period = ELM_MODE_KEEPALIVE_PERIOD_DEFAULT;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const("\r\r");
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const(IDENT_MSG);
							 | 
						||
| 
								 | 
							
								    panda_set_safety_mode(0xE327);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    elm_proto_reinit(elm_current_proto());
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  default:
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const("?\r\r");
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  elm_append_rsp_const("OK\r\r");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*************************************
							 | 
						||
| 
								 | 
							
								 *** Connection and cli management ***
							 | 
						||
| 
								 | 
							
								 *************************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_append_in_msg(char *data, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  if(in_msg_len + len > sizeof(in_msg))
							 | 
						||
| 
								 | 
							
								    len = sizeof(in_msg) - in_msg_len;
							 | 
						||
| 
								 | 
							
								  memcpy(in_msg + in_msg_len, data, len);
							 | 
						||
| 
								 | 
							
								  in_msg_len += len;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int ICACHE_FLASH_ATTR elm_msg_is_at_cmd(char *data, uint16_t len){
							 | 
						||
| 
								 | 
							
								  return len >= 4 && data[0] == 'A' && data[1] == 'T';
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void ICACHE_FLASH_ATTR elm_rx_cb(void *arg, char *data, uint16_t len) {
							 | 
						||
| 
								 | 
							
								  #ifdef ELM_DEBUG
							 | 
						||
| 
								 | 
							
								  //os_printf("\nGot ELM Data In: '%s'\n", data);
							 | 
						||
| 
								 | 
							
								  #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  rsp_buff_len = 0;
							 | 
						||
| 
								 | 
							
								  len = elm_msg_find_cr_or_eos(data, len);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(loopcount){
							 | 
						||
| 
								 | 
							
								    os_timer_disarm(&elm_timeout);
							 | 
						||
| 
								 | 
							
								    loopcount = 0;
							 | 
						||
| 
								 | 
							
								    got_msg_this_run = false;
							 | 
						||
| 
								 | 
							
								    can_tx_worked = false;
							 | 
						||
| 
								 | 
							
								    did_multimessage = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    os_printf("Interrupting operation, stopping timer. msg len: %d\n", len);
							 | 
						||
| 
								 | 
							
								    elm_append_rsp_const("STOPPED\r\r>");
							 | 
						||
| 
								 | 
							
								    if(len == 1 && data[0] == '\r') {
							 | 
						||
| 
								 | 
							
								      os_printf("Empty msg source of interrupt.\n");
							 | 
						||
| 
								 | 
							
								      elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(!(len == 1 && data[0] == '\r') && in_msg_len && in_msg[in_msg_len-1] == '\r'){
							 | 
						||
| 
								 | 
							
								    in_msg_len = 0;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(!(len == 1 && data[0] == '\r' && in_msg_len && in_msg[in_msg_len-1] == '\r')) {
							 | 
						||
| 
								 | 
							
								    // Not Repeating last message
							 | 
						||
| 
								 | 
							
								    elm_append_in_msg(data, len); //Aim to remove this memcpy
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if(elm_mode_echo)
							 | 
						||
| 
								 | 
							
								    elm_append_rsp(in_msg, in_msg_len);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(in_msg_len > 0 && in_msg[in_msg_len-1] == '\r') { //Got a full line
							 | 
						||
| 
								 | 
							
								    stripped_msg_len = elm_strip(in_msg, in_msg_len, stripped_msg, sizeof(stripped_msg));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(elm_msg_is_at_cmd(stripped_msg, stripped_msg_len)) {
							 | 
						||
| 
								 | 
							
								      elm_process_at_cmd(stripped_msg+2, stripped_msg_len-2);
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const(">");
							 | 
						||
| 
								 | 
							
								    } else if(elm_check_valid_hex_chars(stripped_msg, stripped_msg_len - 1)) {
							 | 
						||
| 
								 | 
							
								      elm_current_proto()->process_obd(elm_current_proto(), stripped_msg, stripped_msg_len);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      elm_append_rsp_const("?\r\r>");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  elm_tcp_tx_flush();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  //Just clear the buffer if full with no termination
							 | 
						||
| 
								 | 
							
								  if(in_msg_len == sizeof(in_msg) && in_msg[in_msg_len-1] != '\r')
							 | 
						||
| 
								 | 
							
								    in_msg_len = 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_tcp_disconnect_cb(void *arg){
							 | 
						||
| 
								 | 
							
								  struct espconn *pesp_conn = (struct espconn *)arg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  elm_tcp_conn_t * prev = NULL;
							 | 
						||
| 
								 | 
							
								  for(elm_tcp_conn_t *iter = connection_list; iter != NULL; iter=iter->next){
							 | 
						||
| 
								 | 
							
								    struct espconn *conn = iter->conn;
							 | 
						||
| 
								 | 
							
								    //SHOW_CONNECTION("Considering Disconnecting", conn);
							 | 
						||
| 
								 | 
							
								    if(!memcmp(pesp_conn->proto.tcp->remote_ip, conn->proto.tcp->remote_ip, 4) &&
							 | 
						||
| 
								 | 
							
								       pesp_conn->proto.tcp->remote_port == conn->proto.tcp->remote_port){
							 | 
						||
| 
								 | 
							
								      os_printf("Deleting ELM Connection!\n");
							 | 
						||
| 
								 | 
							
								      if(prev){
							 | 
						||
| 
								 | 
							
								        prev->next = iter->next;
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        connection_list = iter->next;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      os_free(iter);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    prev = iter;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(connection_list == NULL) {
							 | 
						||
| 
								 | 
							
								    //If all clients are disconnected, reset the protocol (cancels
							 | 
						||
| 
								 | 
							
								    //keep alive timers). This will not detect inproperly killed
							 | 
						||
| 
								 | 
							
								    //connections. In this case, periodic events associated with the
							 | 
						||
| 
								 | 
							
								    //current protocol will continue until a new client attaches, a
							 | 
						||
| 
								 | 
							
								    //command is sent generating a response (ELM will try to responde
							 | 
						||
| 
								 | 
							
								    //to the dead connection, and remove it upon error), and finally,
							 | 
						||
| 
								 | 
							
								    //the new client disconnects. OFC a power cycle is also an option.
							 | 
						||
| 
								 | 
							
								    elm_proto_reinit(elm_current_proto());
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm_tcp_connect_cb(void *arg) {
							 | 
						||
| 
								 | 
							
								  struct espconn *pesp_conn = (struct espconn *)arg;
							 | 
						||
| 
								 | 
							
								  //SHOW_CONNECTION("New connection", pesp_conn);
							 | 
						||
| 
								 | 
							
								  espconn_set_opt(&elm_conn, ESPCONN_NODELAY);
							 | 
						||
| 
								 | 
							
								  espconn_regist_recvcb(pesp_conn, elm_rx_cb);
							 | 
						||
| 
								 | 
							
								  //Allow several sends to be queued at a time.
							 | 
						||
| 
								 | 
							
								  espconn_tcp_set_buf_count(pesp_conn, 3);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  bool connection_address_already_there = false;
							 | 
						||
| 
								 | 
							
								  for(elm_tcp_conn_t *iter2 = connection_list; iter2 != NULL; iter2 = iter2->next)
							 | 
						||
| 
								 | 
							
								    if(iter2->conn == pesp_conn){connection_address_already_there = true; break;}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(connection_address_already_there) {
							 | 
						||
| 
								 | 
							
								    os_printf("ELM WIFI: Memory reuse of recently killed connection\n");
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    os_printf("ELM WIFI: Adding connection\n");
							 | 
						||
| 
								 | 
							
								    elm_tcp_conn_t *newconn = os_malloc(sizeof(elm_tcp_conn_t));
							 | 
						||
| 
								 | 
							
								    if(!newconn) {
							 | 
						||
| 
								 | 
							
								      os_printf("Failed to allocate place for connection\n");
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      newconn->next = connection_list;
							 | 
						||
| 
								 | 
							
								      newconn->conn = pesp_conn;
							 | 
						||
| 
								 | 
							
								      connection_list = newconn;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void ICACHE_FLASH_ATTR elm327_init() {
							 | 
						||
| 
								 | 
							
								  // control listener
							 | 
						||
| 
								 | 
							
								  elm_proto.local_port = ELM_PORT;
							 | 
						||
| 
								 | 
							
								  elm_conn.type = ESPCONN_TCP;
							 | 
						||
| 
								 | 
							
								  elm_conn.state = ESPCONN_NONE;
							 | 
						||
| 
								 | 
							
								  elm_conn.proto.tcp = &elm_proto;
							 | 
						||
| 
								 | 
							
								  espconn_regist_connectcb(&elm_conn, elm_tcp_connect_cb);
							 | 
						||
| 
								 | 
							
								  espconn_regist_disconcb(&elm_conn, elm_tcp_disconnect_cb);
							 | 
						||
| 
								 | 
							
								  espconn_accept(&elm_conn);
							 | 
						||
| 
								 | 
							
								  espconn_regist_time(&elm_conn, 0, 0); // 60s timeout for all connections
							 | 
						||
| 
								 | 
							
								}
							 |