@TPTsys Victory!
I was able to receive, mask and filter out the PGN I needed.
setup() code:
// Most vehicles use 500 kbit/sec for OBD-II, j1939 is 250 kbit/sec
// Make sure the last parameter is MCP_20MHZ; this is dependent on the crystal
// connected to the CAN chip and it's 20 MHz on the Tracker SoM.
byte status = canInterface.begin(MCP_RX_STDEXT, CAN_250KBPS, MCP_20MHZ);
if (status == CAN_OK)
{
Log.info("CAN initialization succeeded");
}
else
{
Log.error("CAN initialization failed %d", status);
}
// Change to normal mode to allow messages to be transmitted. If you don't do this,
// the CAN chip will be in loopback mode.
// canInterface.setMode(MCP_MODE_LISTENONLY);
canInterface.setMode(MCP_MODE_NORMAL);
// set up the mask and filters for RXB0 (reception buffer 0)
// init_mask(): I believe the second parameter is the extended ID flag
// since we want to receive extended IDs, we set it to 1
// a mask bit 0 means let it through, 1 means pay attention to filters
// filters 0 and 1 are used for RXB0
// canInterface.init_Mask(0, 1, 0);
// FILTER: 0x03FFFF00 => we filter out the priority bits at the beginning with the 03,
// and the address bits at the end with the 00
// MASK: 0x00f00400 => we mask out the f004 pgn, which in decimal is 61444
canInterface.init_Mask(0, 1, 0x03FFFF00);
canInterface.init_Filt(0, 1, 0x00f00400);
canInterface.init_Filt(1, 1, 0);
// set up the mask and filters for RXB1 (reception buffer 1)
// filters 2 to 5 are used for RXB1
// canInterface.init_Mask(1, 1, 0);
// THIS ONE IS NOT USED!
canInterface.init_Mask(1, 1, 0xFFFFFFFF);
canInterface.init_Filt(2, 1, 0);
canInterface.init_Filt(3, 1, 0);
canInterface.init_Filt(4, 1, 0);
canInterface.init_Filt(5, 1, 0);
loop() code:
// Handle received CAN data
if (!digitalRead(CAN_INT))
{
Log.info("CAN data available");
long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
// on a 29 bit can extended frame identifier, which we get on rxId
// priority is the first 3 bits
// 0x1C0 is 110 0000 000
// j1939 Parameter Group Number or pgn is the next 18 bits
// address is the last 8 bits
int priority = 0;
int pgn = 0;
int address = 0;
canInterface.readMsgBufID(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
priority = (rxId >> 26) & 0x07;
pgn = (rxId >> 8) & 0x3FFFF;
address = (rxId & 0xFF);
Log.info("j1939 rxId: %08lx msg: %02x %02x %02x %02x %02x %02x %02x %02x", rxId,
rxBuf[0], rxBuf[1], rxBuf[2], rxBuf[3], rxBuf[4], rxBuf[5], rxBuf[6], rxBuf[7]);
Log.info("j1939 priority: %d " PRIO_TO_BINARY_PATTERN, priority, PRIO_TO_BINARY(priority));
Log.info("j1939 pgn: %d " PGN_TO_BINARY_PATTERN, pgn, PGN_TO_BINARY(pgn));
Log.info("j1939 address: %d " BYTE_TO_BINARY_PATTERN, address, BYTE_TO_BINARY(address));
// EEC1 electronic engine controller 1 PGN 0xF004 => decimal 61444
int J1939engineSpeed = 61444;
if (pgn == J1939engineSpeed)
{
// RPM comes from SPN 190 (it's called engine speed)
// uses bytes 4 and 5 (indexed starting at 1)
// so in this code (index starts at 0) uses bytes 3 and 4
// little endian, so we need to reorder the byte sequence
// PGN is 0xF004, decimal 61444
// scale is 0.125 (that's why is divided by 8)
// offset is 0 (nothing to do here)
// https://www.csselectronics.com/pages/j1939-explained-simple-intro-tutorial
lastRPM = (rxBuf[4] << 8) | rxBuf[3];
lastRPM /= 8; // scale is 0.125
Log.info("j1939 received rpm: %d", lastRPM);
// We don't process the RPM here, it's done below (with an explanation why)
}
}
Some defines for printing the bits (these go at the beginning of the main.cpp):
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \
(byte & 0x80 ? '1' : '0'), \
(byte & 0x40 ? '1' : '0'), \
(byte & 0x20 ? '1' : '0'), \
(byte & 0x10 ? '1' : '0'), \
(byte & 0x08 ? '1' : '0'), \
(byte & 0x04 ? '1' : '0'), \
(byte & 0x02 ? '1' : '0'), \
(byte & 0x01 ? '1' : '0')
#define PRIO_TO_BINARY_PATTERN "%c%c%c"
#define PRIO_TO_BINARY(byte) \
(byte & 0x04 ? '1' : '0'), \
(byte & 0x02 ? '1' : '0'), \
(byte & 0x01 ? '1' : '0')
// 18 bits pattern
#define PGN_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
#define PGN_TO_BINARY(byte) \
(byte & 0x20000 ? '1' : '0'), \
(byte & 0x10000 ? '1' : '0'), \
(byte & 0x8000 ? '1' : '0'), \
(byte & 0x4000 ? '1' : '0'), \
(byte & 0x2000 ? '1' : '0'), \
(byte & 0x1000 ? '1' : '0'), \
(byte & 0x800 ? '1' : '0'), \
(byte & 0x400 ? '1' : '0'), \
(byte & 0x200 ? '1' : '0'), \
(byte & 0x100 ? '1' : '0'), \
(byte & 0x80 ? '1' : '0'), \
(byte & 0x40 ? '1' : '0'), \
(byte & 0x20 ? '1' : '0'), \
(byte & 0x10 ? '1' : '0'), \
(byte & 0x08 ? '1' : '0'), \
(byte & 0x04 ? '1' : '0'), \
(byte & 0x02 ? '1' : '0'), \
(byte & 0x01 ? '1' : '0')
Sources:
MCP25625-CAN-Controller-Data-Sheet.pdf
j1939-pgn-spn tutorial
This can get you going on your side I believe.
That was quite a fight.
Gustavo.
PS: many thanks to the snowy weather that toasted my outdoor plans.