CAN-OBD-II_PIDs_TEST_CODE LOOVEE @ JUN24, 2017 CAN bus write and read with attachInterrupt fails

We have two sketches that use interrupts to read CAN messages. The first sends messages that don't request responses, but it does read the two unsolicited repetitive messages that come every 1/2 second. It runs for a minute then stops recognizing interrupts. The other sketch (attached) sends messages that request specific information from the CAN bus (e.g. RPM, temp, load). This sketch is a modified version of a sketch that looped looking for a message. It now uses interrupts to recognize a response has been sent. With the attached sketch that uses the attachInterrupt command, the first request receives 2 replies, then there are no further interrupts detected. Is it possible there is something in the *.h, *.dfs or *.cpp files, or with the attachInterrupt function that fails when sending CAN messages as well as reading CAN messages? Or are we misusing the CAN or interrupt commands?

The attached sketch that now uses interrupts to detect incoming messages, is a modified version of the "looping" version found at: Seeed_Arduino_CAN/OBDII_PIDs.ino at master · Seeed-Studio/Seeed_Arduino_CAN · GitHub We have used the associated .h and .cpp and .dfs files.

We have specified the interrupt number 0 (Digital Pin 2 - INT4) in the attachInterrupt command because "digitalPinToInterrupt(x)" does not compile correctly in IDE 1.8.5.

The program output looks like this, indicating that the first sent message (PID 5) got a response, but not the following messages (C, D and 5):

</>
Enter setting mode success
set rate success!!
Enter Normal Mode Success!!
CAN BUS Shield init ok!
Begin to set Mask!!
set Mask success!!
....
Interrupt set MCP2515 Falling
SEND PID: 0x5
PID_INPUT=5
Interrupt Counter = 1


Get Data From id: 0x7E8
0x3 0x41 0x5 0x41 0x0 0x0 0x0 0x0
Interrupt Counter = 2


Get Data From id: 0x7EA
0x3 0x41 0x5 0x41 0x0 0x0 0x0 0x0
SEND PID: 0xC
PID_INPUT=C
SEND PID: 0xD
PID_INPUT=D
SEND PID: 0x5
PID_INPUT=5

/************************************************************************************************* 
  CAN-OBD-II_PIDs_TEST_CODE
  LOOVEE @ JUN24, 2017
  
  Query
  send id: 0x7df
      dta: 0x02, 0x01, PID_CODE, 0, 0, 0, 0, 0
  Response
  From id: 0x7E9 or 0x7EA or 0x7EB
      dta: len, 0x41, PID_CODE, byte0, byte1(option), byte2(option), byte3(option), byte4(option)
      
  https://en.wikipedia.org/wiki/OBD-II_PIDs
  
  Input a PID, then you will get reponse from vehicle, the input should be end with '\n'
***************************************************************************************************/
#include <SPI.h>
#include "mcp_can.h"
unsigned char Flag_Recv = 0;
unsigned long Flag_Recv_Ctr = 0;							// debugging only
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;									// Normally D9, but D11 for DFPCI

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

#define PID_ENGIN_PRM       0x0C
#define PID_VEHICLE_SPEED   0x0D
#define PID_COOLANT_TEMP    0x05
#define PID_ENGINE_LOAD     0x04
#define CAN_ID_PID          0x7DF

unsigned char PID_INPUT;
unsigned char getPid    = 0;

void set_mask_filt()
{
    /*
     * set mask, set both the mask to 0x3ff
     */
    CAN.init_Mask(0, 0, 0x7FC);
    CAN.init_Mask(1, 0, 0x7FC);

    /*
     * set filter, we can receive id from 0x04 ~ 0x09
     */
    CAN.init_Filt(0, 0, 0x7E8);                 
    CAN.init_Filt(1, 0, 0x7E8);

    CAN.init_Filt(2, 0, 0x7E8);
    CAN.init_Filt(3, 0, 0x7E8);
    CAN.init_Filt(4, 0, 0x7E8); 
    CAN.init_Filt(5, 0, 0x7E8);
}

void setup()
{
    Serial.begin(115200);
    while (CAN_OK != CAN.begin(CAN_500KBPS))    // init can bus : baudrate = 500k
    {
        Serial.println("CAN BUS Shield init fail");
        Serial.println(" Init CAN BUS Shield again");
        delay(100);
	}
    Serial.println("CAN BUS Shield init ok!");
    set_mask_filt();
	attachInterrupt(0, MCP2515_ISR, FALLING);     // start interrupt on digital pin 2 (INT4)
		Serial.println("Interrupt set MCP2515 Falling");	
}
void MCP2515_ISR()
{	Flag_Recv = 1;  
	Flag_Recv_Ctr = Flag_Recv_Ctr + 1;		// debugging only
}


void loop()
{	if(Flag_Recv)							// check if an interrupt was received if reading messages only after an interrupt
	{
	Flag_Recv = 0;
	taskCanRecv();								// if msg available, get and print it
	}
	taskDbg();									// go see if any request entered on the keyboard   
	
    if(getPid == 1)          					// GET A PID and send a message to the CAN bus
		{
		Serial.print("getPid=");				// debugging only
		Serial.println(getPid, DEC);			// debugging only	
        getPid = 0;
        sendPid(PID_INPUT);						// send a message because of request from keyboard
		Serial.print("PID_INPUT=");				// debugging only
		Serial.println(PID_INPUT, HEX);			// debugging only	
		PID_INPUT = 0;
		}
}

void taskCanRecv()
{
    unsigned char len = 0;
    unsigned char buf[8];
	
    if(CAN_MSGAVAIL == CAN.checkReceive())		// check if get data
		{
		Serial.print("Interrupt Counter = ");	// debugging only
		Serial.println(Flag_Recv_Ctr, DEC);		// debugging only
		CAN.readMsgBuf(&len, buf);				// read data,  len: data length, buf: data buf

        Serial.println("\r\n------------------------------------------------------------------");
        Serial.print("Get Data From id: 0x");
        Serial.println(CAN.getCanId(), HEX);
        for(int i = 0; i<len; i++)    			// print the data
			{
            Serial.print("0x");
            Serial.print(buf[i], HEX);
            Serial.print("\t");
			}
        Serial.println();
		}  
		else Serial.println("Interrupt received but no message");	// debugging only
}

void taskDbg()								// Read PID from Serial Monitor
{
    while(Serial.available())
    {										// calculate the HEX code for the appropriate PID
        char c = Serial.read();
        
        if(c>='0' && c<='9')				// examine entry from serial monitor
        {
            PID_INPUT *= 0x10;
            PID_INPUT += c-'0';				// leave only the HEX value from 00 to 09
        }
        else if(c>='A' && c<='F')
        {
            PID_INPUT *= 0x10;
            PID_INPUT += 10+c-'A';			// leave only the HEX value from 0A to 0F
        }
        else if(c>='a' && c<='f')
        {
            PID_INPUT *= 0x10;
            PID_INPUT += 10+c-'a';			// leave only the HEX value from 0A to 0F
        }
        else if(c == '\n')      // END
        {
            getPid = 1;
        }
    }
}

void sendPid(unsigned char __pid)
{
    unsigned char tmp[8] = {0x02, 0x01, __pid, 0, 0, 0, 0, 0};
    Serial.print("SEND PID: 0x");
    Serial.println(__pid, HEX);
    CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp);
}

</>
CAN-Final_Example_TEST.ino (4.75 KB)

keywords.txt (1.21 KB)

mcp_can.cpp (42.2 KB)

mcp_can.h (7.41 KB)

mcp_can_dfs.h (12.2 KB)

Race condition involving Flag_Recv.

Ctrl-T would be helpful.

Any variables used inside your ISR must be declared volatile or the compiler is free to optimize things that may break your code

volatile unsigned char Flag_Recv = 0;
volatile unsigned long Flag_Recv_Ctr = 0;              // debugging only

@blh64 Changed the declarations, but it didn't improve things. Interestingly, I get two CAN messages first time opening the serial monitor, then only one the next time, and this pattern repeats. So there is definitely something "volatile" in this set up! Maybe it's the race condition, but I have no idea how to fix that, because locking Flag_Recv in the interrupt routine would prevent the message-read routine from resetting the flag to zero, and locking it in the message-read routine would prevent the interrupt routing from setting the interrupt flag, so an interrupt would be missed. What should be locked, where and how?

@Coding Badly said Ctrl-T would be helpful. Could you please elaborate, as I can't find anything on the subject?

why one earth are you even bothering to use interrupts! Have you seen the examples that come with the library to READ the CAN messages . try to include that in your code instead

for what you are doing, I do not think you ACTUALLY need to go thru interrupt. I've done far more intensive stuff with the CAN shield and worked fine without any need to use interrupts.

davidcyr2000:
@Coding Badly said Ctrl-T would be helpful. Could you please elaborate, as I can't find anything on the subject?

https://www.google.com/search?q=arduino+Ctrl-T

davidcyr2000:
...locking it in the message-read routine would prevent the interrupt routing from setting the interrupt flag...

Prevent, no. Delay, yes.

(I assume by "the message-read routine" you mean "loop" because that is the only other reference to the variable.)

@sherzaad This sketch was only to test the CAN bus function using interrupts. We have a substantial amount of rapid response computing to do in our application, hence the preference for interrupts rather than using CPU cycles to check for the presence of messages. However, if we can't get this working, we may have to use the looping method and suffer the timing penalty. It is hard to know how significant that would be at this point.

@Coding Badly - Yes, I meant in "Loop". Would you use Mutex lock, or something else? Would you lock Flag_Recv for the duration of "Loop"? I assume you mean the interrupt routine will still turn ON Flag_Recv once the lock is released so we won't lose the interrupt.

@Coding Badly - Ctrl-T - I had my code indented nicely in Notepad++ where I do my editing, but it seems to have lost it when moving it here. I tried it, and it does a nice job (automatically). Thanks for the heads up!

davidcyr2000:
Would you use Mutex lock...

No operating system = no mutex.

...or something else?

Start reading at "Enabling / disabling interrupts".

Would you lock Flag_Recv for the duration of "Loop"?

Only for the read / reset. You will need a local variable for the value.

I assume you mean the interrupt routine will still turn ON Flag_Recv once the lock is released so we won't lose the interrupt.

Correct.

Added the suggestions to the code: volatile and noInterrupts(); & interrupt(); around the read & reset of the variable used inside the ISR. No change, BUT, I decided remove the mask/filter code to allow the 2 standard CAN messages that arrive ~twice per second when the car is ON. The interrupt function works fine (forever) until I enter a PID on the Serial Monitor. The system responds to the first sent message with the requested information message, but immediately after that all interrupts stop coming, even for the unsolicited messages. So it seems the sending of a CAN message kills the interrupt process somehow.

davidcyr2000:
Added the suggestions to the code...

No code = no help. Post the new version.

This is the test: The sketch accepts all CAN messages (no mask/filter). Two unsolicited messages arrive every 1/2 seconds. After a few seconds, a "coolant temp" message is sent by entering "5" into the serial monitor. There is a response to this request, but no more interrupts are received after that. The revised code follows the output below.

This is the sketch output:

Enter setting mode success 
set rate success!!
Enter Normal Mode Success!!
CAN BUS Shield init ok!
Interrupt set MCP2515 Falling
Interrupt Counter = 1

------------------------------------------------------------------
Get Data From id: 0x45A
0x5A	0x4	0x0	0x0	0x0	0x0	0x0	0x0	
Interrupt Counter = 2

------------------------------------------------------------------
Get Data From id: 0x4E0
0x24	0x0	0x0	0x0	0x0	0x0	0x0	0x0	
Interrupt Counter = 3
.
.
.
------------------------------------------------------------------
Get Data From id: 0x4E0
0x24	0x0	0x0	0x0	0x0	0x0	0x0	0x0	
getPid=1
SEND PID: 0x5
PID_INPUT=5
Interrupt Counter = 15

------------------------------------------------------------------
Get Data From id: 0x7E8
0x3	0x41	0x5	0x41	0x0	0x0	0x0	0x0	
Interrupt Counter = 16

------------------------------------------------------------------
Get Data From id: 0x7EA
0x3	0x41	0x5	0x41	0x0	0x0	0x0	0x0	
getPid=1
SEND PID: 0xC
PID_INPUT=C
getPid=1
SEND PID: 0xD
PID_INPUT=D

***** OUTPUT STOPS HERE *****

This is the test code:

/*************************************************************************************************
  CAN-OBD-II_PIDs_TEST_CODE
  LOOVEE @ JUN24, 2017

  Query
  send id: 0x7df
      dta: 0x02, 0x01, PID_CODE, 0, 0, 0, 0, 0
  Response
  From id: 0x7E9 or 0x7EA or 0x7EB
      dta: len, 0x41, PID_CODE, byte0, byte1(option), byte2(option), byte3(option), byte4(option)

  https://en.wikipedia.org/wiki/OBD-II_PIDs

  Input a PID, then you will get reponse from vehicle, the input should be end with '\n'
***************************************************************************************************/
#include <SPI.h>
#include "mcp_can.h"
volatile unsigned char Flag_Recv = 0;
unsigned char Flag_Recv_ext = 0;		// local value of Flag_Recv for reading external from ISR so loop processing doesn't stop interrupts too long
volatile unsigned long Flag_Recv_Ctr = 0; // debugging only
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 11;				// Normally D9, but D11 for DFPCI

MCP_CAN CAN(SPI_CS_PIN);				// Set CS pin

#define PID_ENGIN_PRM       0x0C
#define PID_VEHICLE_SPEED   0x0D
#define PID_COOLANT_TEMP    0x05
#define CAN_ID_PID          0x7DF
unsigned char PID_INPUT;
unsigned char getPid    = 0;

void setup()
{
  Serial.begin(115200);
  while (CAN_OK != CAN.begin(CAN_500KBPS))    // init can bus : baudrate = 500k
  {
    Serial.println("CAN BUS Shield init fail");
    Serial.println(" Init CAN BUS Shield again");
    delay(100);
  }
  Serial.println("CAN BUS Shield init ok!");
  attachInterrupt(6, MCP2515_ISR, FALLING);     // start interrupt on pin D81 (INT6)
  Serial.println("Interrupt set MCP2515 Falling");
}
void MCP2515_ISR()
{ Flag_Recv = 1;
  Flag_Recv_Ctr = Flag_Recv_Ctr + 1; // debugging only
}
void loop()
{
  noInterrupts ();				// Added this to see if conflict between threads
  Flag_Recv_ext = Flag_Recv;
  interrupts ();					// Added this to see if conflict between threads
  if (Flag_Recv_ext)					// check if an interrupt was received if reading messages only after an interrupt
  {
    noInterrupts ();				// Added this to see if conflict between threads
    Flag_Recv = 0;
    interrupts ();					// Added this to see if conflict between threads
    taskCanRecv();					// if msg available, get and print it
  }

  taskDbg();						// go see if any request entered on the Serial Monitor keyboard
  if (getPid == 1)					// GET A PID and send a message to the CAN bus
  {
    Serial.print("getPid=");		// debugging only
    Serial.println(getPid, DEC);	// debugging only
    getPid = 0;
    sendPid(PID_INPUT);				// send a message because of request from keyboard
    Serial.print("PID_INPUT=");		// debugging only
    Serial.println(PID_INPUT, HEX);	// debugging only
    PID_INPUT = 0;
  }
}
void taskCanRecv()
{
  unsigned char len = 0;
  unsigned char buf[8];
  if (CAN_MSGAVAIL == CAN.checkReceive())	// check if get data
  {
    Serial.print("Interrupt Counter = ");	// debugging only
    Serial.println(Flag_Recv_Ctr, DEC);		// debugging only
    CAN.readMsgBuf(&len, buf);				// read data,  len: data length, buf: data buf
    Serial.println("\r\n------------------------------------------------------------------");
    Serial.print("Get Data From id: 0x");
    Serial.println(CAN.getCanId(), HEX);
    for (int i = 0; i < len; i++)			// print the data
    {
      Serial.print("0x");
      Serial.print(buf[i], HEX);
      Serial.print("\t");
    }
    Serial.println();
  }
  else Serial.println("Interrupt received but no message");	// debugging only
}
void taskDbg()						// Read PID from Serial Monitor
{
  while (Serial.available())		// calculate the HEX code of the entered PID
  {
    char c = Serial.read();
    if (c >= '0' && c <= '9')		// examine entry from serial monitor
    {
      PID_INPUT *= 0x10;
      PID_INPUT += c - '0';			// leave only the HEX value from 00 to 09
    }
    else if (c >= 'A' && c <= 'F')
    {
      PID_INPUT *= 0x10;
      PID_INPUT += 10 + c - 'A';	// leave only the HEX value from 0A to 0F
    }
    else if (c >= 'a' && c <= 'f')
    {
      PID_INPUT *= 0x10;
      PID_INPUT += 10 + c - 'a';	// leave only the HEX value from 0A to 0F
    }
    else if (c == '\n')				// END
    {
      getPid = 1;
    }
  }
}
void sendPid(unsigned char __pid)
{
  unsigned char tmp[8] = {0x02, 0x01, __pid, 0, 0, 0, 0, 0};
  Serial.print("SEND PID: 0x");
  Serial.println(__pid, HEX);
  CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp);
}

Your critical section is worthless. You are protecting bits of code that are already atomic. Unfortunately, I assumed you understand the goal and you failed to ask about something you don't understand.

A (hopefully) better wording...

Only for the read AND reset which have to be done TOGETHER inside of a critical section. You will need a local variable for the value. While a global variable can be used it is a waste of memory.

Sorry, I was trying to help myself by reading about critical sections and the way to protect variables. Apparently I didn't understand the requirement or the solution. Would this work better? If not, would you please be so kind as to suggest something a little more specific so I get it? I have only altered the code inside LOOP. Same result. Thanks!

void MCP2515_ISR()
{ Flag_Recv = 1;
  Flag_Recv_Ctr = Flag_Recv_Ctr + 1; // debugging only
}
void loop()
{
  noInterrupts ();
  unsigned char Flag_Recv_ext = Flag_Recv;
  if (Flag_Recv_ext) // check if an interrupt was received
  {
    Flag_Recv = 0;        // Reset Flag_Recv
    interrupts (); // turn ON interrupts after reset of Flag_Recv
    taskCanRecv(); // if msg available, get and print it
  }
  if (Flag_Recv_ext != 1) {
    interrupts ();        // turn ON interrupts if not turned ON after Reset
  }
  taskDbg();   // go see if any request entered on the Serial Monitor keyboard
  if (getPid == 1) // GET A PID and send a message to the CAN bus
  {
    Serial.print("getPid="); // debugging only
    Serial.println(getPid, DEC); // debugging only
    getPid = 0;
    sendPid(PID_INPUT); // send a message because of request from keyboard
    Serial.print("PID_INPUT="); // debugging only
    Serial.println(PID_INPUT, HEX); // debugging only
    PID_INPUT = 0;
  }
}

It should work but it is unnecessarily complicated. Try this...

...
void loop( void )
{
  noInterrupts ();
  unsigned char Flag_Recv_ext = Flag_Recv;
  Flag_Recv = 0;        // Reset Flag_Recv
  interrupts (); // turn ON interrupts after reset of Flag_Recv

  // Flag_Recv_ext holds the flag's value.
  // Do not touch Flag_Recv.
  // Interrupts are enabled.  Any calls to interrupts after this point should be removed.

  if (Flag_Recv_ext) // check if an interrupt was received
  {
    ...

How is the falling signal generated?

davidcyr2000:
@sherzaad This sketch was only to test the CAN bus function using interrupts. We have a substantial amount of rapid response computing to do in our application, hence the preference for interrupts rather than using CPU cycles to check for the presence of messages. However, if we can't get this working, we may have to use the looping method and suffer the timing penalty. It is hard to know how significant that would be at this point.

your approach is fundametally flawed

Interrupt from MCP2515 implies data is available to be collected.

You are using it to set a flag.

in your loop you are checking for the flag and to me this is the problem!

the interrupt could have happened anywhere in the loop, e.g. possibly while executing taskDbg(); - so it would need to complete the loop before checking for the flag again... which boils does to just polling the INTR pin to see if data is available! Speak of a waste!!!

IMHO what you should do is call CAN.readMsgBuf(&len, buf) in your INTR but it DEFINITELY will complicate stuff (as in more code to add to manage the data)... I know first hand having done such a code myself but as I originally posted just polling (as in the example with the library) is/was just as good

@sherzaad the interrupt could have happened anywhere in the loop, e.g. possibly while executing taskDbg(); - so it would need to complete the loop before checking for the flag again... which boils does to just polling the INTR pin to see if data is available! Speak of a waste!!!

When an interrupt arrives while looping (normal), it will be queued and detected the next time through the loop, which is what it should do. Same with a second message while in the same pass through the loop; it gets queued. However, while there are no messages (interrupts) arriving, the only overhead the sketch has is a quick IF. Otherwise with looping, the call to "if (CAN_MSGAVAIL == CAN.checkReceive())" surely takes many more cycles. Secondly, testing the interrupt pin may not work properly if the loop takes too long to get around to reading the interrupt pin again, the pin may have gone HIGH, so the interrupt will be missed. Anyway, we may be forced into this if we don't find the fix for the interrupt approach.

@Coding Badly - Used your code but get the same output. So I removed all interrupt code and added it back in, bit by bit, to see what exactly is causing the interrupts to stop coming. That statement is the IF: if (Flag_Recv_ext). By removing the IF, the program runs more or less () as expected. I inserted prints to show the value of Flag_Recv_ext, and it is 1 when it should be and zero when not. The output below is with the IF removed.
(
) I wouldn't expect the requested messages to repeat 3 or 4 times each!

Enter setting mode success 
set rate success!!
Enter Normal Mode Success!!
CAN BUS Shield init ok!
Interrupt set MCP2515 Falling
Flag_Recv_ext=1
Interrupt Counter = 1

------------------------------------------------------------------
Get Data From id: 0x4E0
0x24	0x0	0x0	0x0	0x0	0x0	0x0	0x0	
Flag_Recv_ext=1
Interrupt Counter = 2

------------------------------------------------------------------
Get Data From id: 0x45A
0x5A	0x4	0x0	0x0	0x0	0x0	0x0	0x0	
Flag_Recv_ext=1
Interrupt Counter = 3

------------------------------------------------------------------
Get Data From id: 0x4E0
0x24	0x0	0x0	0x0	0x0	0x0	0x0	0x0	
Interrupt Counter = 4

------------------------------------------------------------------
Get Data From id: 0x45A
0x5A	0x4	0x0	0x0	0x0	0x0	0x0	0x0	
Flag_Recv_ext=1
Flag_Recv_ext=1
Interrupt Counter = 5

------------------------------------------------------------------
Get Data From id: 0x4E0
0x24	0x0	0x0	0x0	0x0	0x0	0x0	0x0	
Flag_Recv_ext=1
Interrupt Counter = 6

------------------------------------------------------------------
Get Data From id: 0x45A
0x5A	0x4	0x0	0x0	0x0	0x0	0x0	0x0	
getPid=1
SEND PID: 0x5
PID_INPUT=5
Flag_Recv_ext=1
Interrupt Counter = 7

------------------------------------------------------------------
Get Data From id: 0x7EB
0x3	0x41	0x5	0x53	0x0	0x0	0x0	0x0	
Flag_Recv_ext=1
Interrupt Counter = 8

------------------------------------------------------------------
Get Data From id: 0x7EA
0x3	0x41	0x5	0x53	0x0	0x0	0x0	0x0	
Interrupt Counter = 8

------------------------------------------------------------------
Get Data From id: 0x7EE
0x3	0x41	0x5	0x53	0x0	0x0	0x0	0x0	
Interrupt Counter = 8

------------------------------------------------------------------
Get Data From id: 0x7E8
0x3	0x41	0x5	0x53	0x0	0x0	0x0	0x0	
Flag_Recv_ext=1
/*************************************************************************************************
  CAN-OBD-II_PIDs_TEST_CODE
  LOOVEE @ JUN24, 2017

  Query
  send id: 0x7df
      dta: 0x02, 0x01, PID_CODE, 0, 0, 0, 0, 0
  Response
  From id: 0x7E9 or 0x7EA or 0x7EB
      dta: len, 0x41, PID_CODE, byte0, byte1(option), byte2(option), byte3(option), byte4(option)

  https://en.wikipedia.org/wiki/OBD-II_PIDs

  Input a PID, then you will get reponse from vehicle, the input should be end with '\n'
***************************************************************************************************/
#include <SPI.h>
#include "mcp_can.h"
volatile unsigned char Flag_Recv = 0;
volatile unsigned long Flag_Recv_Ctr = 0; // debugging only
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 11;				// Normally D9, but D11 for DFPCI

MCP_CAN CAN(SPI_CS_PIN);				// Set CS pin

#define PID_ENGIN_PRM       0x0C
#define PID_VEHICLE_SPEED   0x0D
#define PID_COOLANT_TEMP    0x05
#define CAN_ID_PID          0x7DF
unsigned char PID_INPUT;
unsigned char getPid    = 0;

void setup()
{
  Serial.begin(115200);
  while (CAN_OK != CAN.begin(CAN_500KBPS))    // init can bus : baudrate = 500k
  {
    Serial.println("CAN BUS Shield init fail");
    Serial.println(" Init CAN BUS Shield again");
    delay(100);
  }
  Serial.println("CAN BUS Shield init ok!");
  attachInterrupt(0, MCP2515_ISR, FALLING);     // start interrupt on pin D81 (INT6)
  Serial.println("Interrupt set MCP2515 Falling");
}
void MCP2515_ISR()
{ Flag_Recv = 1;
  Flag_Recv_Ctr = Flag_Recv_Ctr + 1; // debugging only
}

void loop( void )
{
  noInterrupts ();
  unsigned char Flag_Recv_ext = Flag_Recv;	// Flag_Recv_ext holds the Flag_Recv's value
  Flag_Recv = 0;      						// Reset Flag_Recv
  interrupts ();							// turn ON interrupts after reset of Flag_Recv
  if (Flag_Recv_ext != 0)
  { Serial.print("Flag_Recv_ext=");
    Serial.println(Flag_Recv_ext, DEC);
  }
  //  if (Flag_Recv_ext)							// check if an interrupt was received
  {
    taskCanRecv();							// if msg available, get and print it
  }
  taskDbg();					  	// go see if any request entered on the Serial Monitor keyboard
  if (getPid == 1)					// GET A PID and send a message to the CAN bus
  {
    Serial.print("getPid=");		// debugging only
    Serial.println(getPid, DEC);	// debugging only
    getPid = 0;
    sendPid(PID_INPUT);				// send a message because of request from keyboard
    Serial.print("PID_INPUT=");		// debugging only
    Serial.println(PID_INPUT, HEX);	// debugging only
    PID_INPUT = 0;
  }
}
void taskCanRecv()
{
  unsigned char len = 0;
  unsigned char buf[8];
  if (CAN_MSGAVAIL == CAN.checkReceive())	// check if get data
  {
    Serial.print("Interrupt Counter = ");	// debugging only
    Serial.println(Flag_Recv_Ctr, DEC);		// debugging only
    CAN.readMsgBuf(&len, buf);				// read data,  len: data length, buf: data buf
    Serial.println("\r\n------------------------------------------------------------------");
    Serial.print("Get Data From id: 0x");
    Serial.println(CAN.getCanId(), HEX);
    for (int i = 0; i < len; i++)			// print the data
    {
      Serial.print("0x");
      Serial.print(buf[i], HEX);
      Serial.print("\t");
    }
    Serial.println();
  }
  //  else Serial.println("Interrupt received but no message");	// debugging only
}
void taskDbg()						// Read PID from Serial Monitor
{
  while (Serial.available())		// calculate the HEX code of the entered PID
  {
    char c = Serial.read();
    if (c >= '0' && c <= '9')		// examine entry from serial monitor
    {
      PID_INPUT *= 0x10;
      PID_INPUT += c - '0';			// leave only the HEX value from 00 to 09
    }
    else if (c >= 'A' && c <= 'F')
    {
      PID_INPUT *= 0x10;
      PID_INPUT += 10 + c - 'A';	// leave only the HEX value from 0A to 0F
    }
    else if (c >= 'a' && c <= 'f')
    {
      PID_INPUT *= 0x10;
      PID_INPUT += 10 + c - 'a';	// leave only the HEX value from 0A to 0F
    }
    else if (c == '\n')				// END
    {
      getPid = 1;
    }
  }
}
void sendPid(unsigned char __pid)
{
  unsigned char tmp[8] = {0x02, 0x01, __pid, 0, 0, 0, 0, 0};
  Serial.print("SEND PID: 0x");
  Serial.println(__pid, HEX);
  CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp);
}

Coding Badly: How is the falling signal generated?

The MCP2515 CAN bus chip has an interrupt pin that is normally HIGH and goes LOW when a masked/filtered message is available.

OK, here is a theory as to why the IF statement makes a difference: Because there seems to be 3 or 4 response messages for each sent message that are read by the checkReceive(), getMsgBuf and getCanId instructions in void taskCanRecv(), when the IF (Flag_Recv_ext) is commented-out, all 3 or 4 messages are removed from the buffers. However, when the IF is included, only the first occurence of the requested message is read, leaving 2 or 3 messages "stuck" in the buffer, unread. This might be the reason the interrupts stop, because we haven't finished servicing the original interrupt by emptying the read buffers.

So, why are there multiple response messages? There aren't interrupts for each of the 3 or 4 messages because the interrupt counter (Flag_Recv_Ctr) inside the ISR does not increment while processing these 3 or 4 identical messages.

Thoughts???