Skip to content

Instantly share code, notes, and snippets.

@agmangas
Last active November 24, 2024 21:26
Show Gist options
  • Save agmangas/4f8ef5febcb7564274f77c49ccbc0ffb to your computer and use it in GitHub Desktop.
Save agmangas/4f8ef5febcb7564274f77c49ccbc0ffb to your computer and use it in GitHub Desktop.
Relationship between CAN IDs and J1939 PGNs

CAN IDs & J1939 PGNs

Global PGN Example

Original CAN ID:

1    8    F    0    0    1    0    B
0001 1000 1111 0000 0000 0001 0000 1011

Separated on each individual part:

Part Value
Priority 110
Reserved 0
Data Page 0
PDU Format 11110000
PDU Specific 00000001
Source Address 00001011

Notice that an Extended CAN Identifier has 29 bits, so the first three leading bits are not taken into account.

As the PDU Format is greater or equal than F0 this is a Global PGN. In this type of PGNs the PDU Specific is appended to the PDU Format to get the final PGN.

000000 (6 bits) + Reserved (1 bit) + Data Page (1 bit) + PDU Format (8 bits) + PDU Specific (8 bits)
0    0    F    0    0    1
0000 0000 1111 0000 0000 0001

The PGN is a 18 bit value.

Converted to decimal: 61441.

Specific PGN Example

Original CAN ID:

0    C    8    2    0    E    0    F
0000 1100 1000 0010 0000 1110 0000 1111

Separated on each individual part:

Part Value
Priority 011
Reserved 0
Data Page 0
PDU Format 10000010
PDU Specific 00001110
Source Address 00001111

As the PDU Format is lower than F0 this is a Specific PGN. These belong to messages that are directed to a specific device (peer-to-peer).

000000 (6 bits) + Reserved (1 bit) + Data Page (1 bit) + PDU Format (8 bits) + 00000000 (8 bits)
0    0    8    2    0    0
0000 0000 1000 0010 0000 0000

Converted to decimal: 33280.

@manuelmj39
Copy link

How do I convert DBC ID to PGN ?

@agmangas
Copy link
Author

By DBC ID, do you mean the CAN ID? The documentation above provides some details on how to do it. Here's an example implementation in Python:

import can


def can_id_to_pgn(can_id):
    """Takes a 29-bit CAN ID and returns the PGN.

    We only support the SAE J1939 Page 0 data page (EDP == 0 and DP == 0).
    The other data pages (NMEA2000, SAE J1939 Reserved and ISO 15765-3) will raise a ValueError.

    :type can_id: int | can.message.Message
    :rtype: int
    :raises ValueError: If the EDP or DP bits are not 0
    """

    can_id = can_id.arbitration_id if isinstance(can_id, can.Message) else can_id

    reserved = can_id >> 25 & 0x1
    data_page = can_id >> 24 & 0x1

    if reserved != 0:
        raise ValueError("Reserved (Extended Data Page) bit for CAN ID {} is != 0".format(hex(can_id)))

    if data_page != 0:
        raise ValueError("Data Page bit for CAN ID {} is != 0".format(hex(can_id)))

    pdu_format = can_id >> 16 & 0xFF
    pdu_specific = can_id >> 8 & 0xFF

    if is_specific_pgn_can_id(can_id):
        return pdu_format << 8
    else:
        return (pdu_format << 8) | pdu_specific


def is_specific_pgn_can_id(can_id):
    """Returns True if the PGN extracted from the given CAN ID is a Specific PGN.

    :type can_id: int
    :rtype: bool
    """

    pdu_format = can_id >> 16 & 0xFF
    return pdu_format < 0xF0

@manuelmj39
Copy link

manuelmj39 commented Oct 12, 2024

Hey. Thanks for the reply! By the DBC ID, I meant the one thats mentioned in J1939 dbc files.
For example, after extracting the PGN from the CAN ID i.e 61441 in the first example, how do we then map it to the item thats mentioned in the J1939 dbc file?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment