I have written a simple program that should send "NO MESSAGE!" to the serial monitor if there are no CAN messages or "A NEW MESSAGE ARRIVED!" and then the CAN message itself through UART2 when there are CAN messages. My idea is that since all car sensors are connected to the same CAN bus then I should be able to read all the messages live if i set all the CAN filter bits to 0, I don't know if that's possible or I have to send requests for specific PID and wait for a response (this is my first design question).
I connected my device to my car's OBD2 port and expected to see a swarm of CAN messages as soon as I turned on the engine, instead all i saw was "no message!" over and over again.
I am aware that there are so many things that could cause the said problem so I am going to explain everything that I have done hoping that someone could tell me what's the problem.
I have tried the loopback mode and I was able to both receive and send CAN messages but that doesn't really tell me much since CAN RX and CAN TX are internally connected so the fact that I can send and receive messages using loopback mode does not mean that the MCP2551 IC that I am using is faulty or not and currently it's the only IC i have and cannot try other ICs. So the first possible problem could be a faulty IC.
I have also seen in many online schematics that people place a 120 ohm resistor between CANH and CANL but they say it's only required if my MCU is a terminal node. I don't know if it is considered a terminal node when i connect it to the OBD2 port or not so I did NOT put a resistor there (will try it on my second try).
I do not have a scope capable of scoping my car's OBD2 port to see if there are signals on CANH and CANL wires although it could be the cause i highly doubt it. here is the picture of my car's OBD port and its connections:
You can clearly see that pins 6 and 14 have wires so my car does support CAN and an unknown protocol (pins 1 and 9).
Next there is the baud rate. According to this website: this there are 4 flavors of CAN datalink layers:
500kbps with standard (11 bit) identifiers
250kbps with standard (11 bit) identifiers
500kbps with extended (29 bit) identifiers
250kbps with extended (29 bit) identifiers
So far I have only used the 500kbps with 11 bit identifiers. Does it matter if it is standard or extended when I’m just sniffing packets?
Here is init CAN setting:
static void MX_CAN1_Init(void) {
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 5;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_15TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = DISABLE;
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = DISABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
Error_Handler();
}
}
Which according to CUBEMX should give me a 500kbit/sec baud rate since my APB1 clock rate is 45 MHz. Which brings me to my third question, how does one change CAN baud rate in the middle of the program? I’m thinking de-init CAN, create another CAN init function with 250 kbit/sec settings and initialize CAN again, is there an easier way?
Here is the main function:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_CAN1_Init();
/* USER CODE BEGIN 2 */
CAN1_Config();
if(HAL_CAN_Start(&hcan1) != HAL_OK)
{
Error_Handler();
}
CAN1_Rx();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Here is my CAN1_config() function:
void CAN1_Config(void)
{
CAN_FilterTypeDef CAN1_CONF;
CAN1_CONF.FilterActivation = ENABLE;
CAN1_CONF.FilterBank = 0;
CAN1_CONF.FilterFIFOAssignment = CAN_RX_FIFO0;
CAN1_CONF.FilterIdHigh = 0X0000;
CAN1_CONF.FilterIdLow = 0X0000;
CAN1_CONF.FilterMaskIdHigh = 0X0000;
CAN1_CONF.FilterMaskIdLow = 0X0000;
CAN1_CONF.FilterMode = CAN_FILTERMODE_IDMASK;
CAN1_CONF.FilterScale = CAN_FILTERSCALE_32BIT;
if(HAL_CAN_ConfigFilter(&hcan1, &CAN1_CONF) != HAL_OK)
{
Error_Handler();
}
}
Here is my CAN1_RX() function:
void CAN1_Rx(void)
{
uint8_t CAN_Rx_Msg[5];
CAN_RxHeaderTypeDef CAN1_RxHeader;
while(1)
{
if(! HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0))
{
HAL_UART_Transmit(&huart2, (unsigned char *)"NO MESSAGE!\n", strlen("NO MESSAGE!\n"), HAL_MAX_DELAY);
HAL_Delay(1500);
}
else
{
HAL_UART_Transmit(&huart2, (unsigned char *)"MESSAGE ARRIVED!\n", strlen("MESSAGE ARRIVED!\n"), HAL_MAX_DELAY);
HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &CAN1_RxHeader, CAN_Rx_Msg);
HAL_UART_Transmit(&huart2, (unsigned char *)CAN_Rx_Msg, sizeof(CAN_Rx_Msg), HAL_MAX_DELAY);
}
}
}
Finally I will draw a quick schematic of my circuit (I can upload real pictures should you guys require it of both the circuit and the OBD2 plug that I am connecting to my car)
I put the 10 k resistor on pin 8 to control slew rate (found it in an online diagram) also I don't know if i must put a 120 ohm resistor between CANH and CANL.
Sorry for the long post and the terrible schematics, this website's tool is just too fancy so I used paint instead :D