1

I would like to get a code example to send a simple CAN message with the J1939 stack using Python.

The following web site has a simple example to receive a J1939 message: https://justkding.me/thoughts/python-sae-j1939-socket-support

The code on this page works great for receiving:

    import socket

def main():
    with socket.socket(
        family=socket.PF_CAN, type=socket.SOCK_DGRAM, proto=socket.CAN_J1939
    ) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        addr = "vcan0", socket.J1939_NO_NAME, socket.J1939_NO_PGN, socket.J1939_NO_ADDR
        s.bind(addr)

        while True:
            data, addr = s.recvfrom(128)
            print("{:02x} {:05x}:".format(addr[3], addr[2]), end="")

            for j in range(len(data)):
                if j % 8 == 0 and j != 0:
                    print("\n{:05x}    ".format(j), end="")
                print(" {:02x}".format(data[j]), end="")
            print("\n", end="")

if __name__ == "__main__":
    main()

I have been reading through the docs, but I can't seem to find a simple way to send a J1939 message in Python.

Here's the reference to the kernel documentation: https://www.kernel.org/doc/html/latest/networking/j1939.html

Here's a C utility code of testj1939 that could be useful: https://github.com/linux-can/can-utils/blob/master/testj1939.c

Can someone post a simple code to send a message using the J1939 protocol in Python? Any documentation that shows how to properly do this would be appreciated.

Thank you.

2 Answers2

1

The solution was to use sendto() instead of send(). In J1939, the bytes given as argument to send() is only the payload. Therefore, sendto() is needed to specify the destination address.

import socket

def main():

    s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_J1939) 
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    
    addr_source = "vcan0", socket.J1939_NO_NAME, socket.J1939_NO_PGN, 0x20
    addr_destination = "vcan0", socket.J1939_NO_NAME, 0x12300, 0x30
    s.bind(addr_source)

    data = b'\x01\x02\x03\x04\x05\x06\x07\x08'
    s.sendto(data, addr_destination)

if __name__ == "__main__":
    main()
0

The examples above treat j1939 as raw can and will not account for actual j1939 protocol operations or messages longer than 8 bytes.

Using python-can and python-j1939 you can have an actual protocol aware interface and a pretty simple code as a result

import time
import j1939

if __name__ == "__main__":

    # code to broadcast a DM1 message at 1 Hz
    # 18FECAFE#FFFF00000000FFFF

    channel = 'can0'
    bustype = 'socketcan'
    sourceaddr = 0xFE
    destaddr = 0xFF


    bus = j1939.Bus(channel=channel, bustype=bustype, timeout=0.01, broadcast=False)

    data = [0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF]
    pgn = j1939.PGN()
    pgn.value = 0xFECA # DM1 
    aid = j1939.ArbitrationID(pgn=pgn, source_address=sourceaddr, destination_address=destaddr)
    pdu = j1939.PDU(timestamp=0.0, arbitration_id=aid, data=data, info_strings=None)

    while True:
        bus.send(pdu) 
        time.sleep(1)
D M Lowe
  • 187
  • 1
  • 6