ARD1939 - SAE J1939 Protocol Stack for Arduino, Teensy, ESP32
by Wilfried Voss, author of SAE J1939 ECU Programming & Vehicle Bus Simulation with Arduino
I deem it necessary to add a few non-technical (and maybe politically incorrect) aspects on the development of the most interesting feature of my book, the ARD1939, an SAE J1939 protocol stack for Arduino.
The implementation of an SAE J1939 protocol stack was (and in many cases still is) out of financial reach for many engineers. The software (i.e. the source code) is either grossly overpriced or comes with hefty royalties (object code, libraries), meaning you have to pay for each copy. At least in the case of the Arduino hardware, this is going to change, and the ARD1939 protocol stack project is available as a free download.
Quite honestly, the development of a J1939 protocol is not a big deal for an experienced programmer, the only obstacle being that you have to spend some good money on the official document, the SAE J1939 Standards Collection. I deem my code as good as any commercially available protocol stacks. And yes, I have successfully tested my code against a number of commercially available J1939 devices.
I had contemplated releasing ARD1939 in form of the original source code but ultimately decided against it, mostly out of respect for those small businesses that make a living from selling SAE J1939 devices and software tools. Instead, I provide a pre-compiled code.
The original source code could be easily adapted to any other embedded hardware (I have it already running on an ARM system) and even Windows or Linux PCs with CAN capabilities.
I believe that the average Arduino user, through using the pre-compiled ARD1939 code, will be quite able to write effective J1939 applications, regardless of whether or not they have access to the original source code. After all, there is no real need to modify a working code. It supports all SAE J1939 protocol features, and the focus should always be on the actual application.
- Arduino Uno
- Arduino Mega 2560
- Arduino Due - For the Arduino Due Version, switch here...
- Teensy - In Preparation
- ESP32 - In Preparation
ARD1939 - Implementation and Technical Insights
All technical information about ARD1939, the SAE J1939 protocol stack for Arduino, plus detailed description of implementation and available function calls is included in SAE J1939 ECU Programming & Vehicle Bus Simulation with Arduino by Wilfried Voss.
Arduino sketches (software projects) include a J1939 network scanner, and a simple SAE J1939 to USB Gateway application with associated Windows GUI (Visual Studio C# project). The collection of sketches is concluded by the ARD1939 project, a fully functional SAE J1939 protocol stack for the Arduino Uno and Mega 2560.
Download ARD1939 and Programming Samples
All programming samples in the book and on this website are free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The samples are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. With downloading these programs, you confirm that these code samples and projects were created for demonstration and educational purpose only.
- ARD1939.zip - Covers most of the projects as described in the book
- J1939_Receiving_Messages.zip - Receiving J1939 Message Frames
- J1939_Request_Message.zip - Receiving and Responding to J1939 Request Frames
- J1939_Stress_Test.zip - Transmitting PGNs into a J1939 network
- Arduino-Serial-Monitor.zip - Visual Studio C# code for simulating the Arduino Serial Monitor
ARD1939 - Function Overview
The programming of every serial communication (such as RS232, CAN, Ethernet, just to name a few) should follow a very simple sequence:
- 1. Initialization
- 2. Read data
- 3. Write data
- 4. Check status
These four function calls should be everything a programmer needs to access the serial protocol, and there is no reason why an SAE J1939 protocol stack implementation should not follow the same scheme. Digging inside complex code in order to understand how to access protocol functions is simply a waste of time.
Unfortunately, most commercially available protocol stacks shine through complexity and/or lack of documentation. To mention it upfront, the ARD1939 protocol stack requires only very few function calls and thus enables the programmer to put the protocol into use in a very short time.
- j1939.Init – Initializes the protocol stack settings
- j1939.SetPreferredAddress – Sets the preferred node (source) address
- j1939.SetNAME – Sets the ECU’s NAME using the individual parameters
- j1939.SetMessageFilter – Sets the PGNs to be processed in your application
Read/Write – Check Status
- j1939.Operate – Handles the address claim process, reads PGNs from the vehicle network, and delivers the current protocol status (Address Claim in progress, Address Claim successful, Address Claim failed)
- j1939.Transmit – Transmits data to the vehicle network and handles the Transport Protocol (TP)
Other Application Functions
- j1939.Terminate – Resets the protocol stack settings
- j1939.GetSourceAddress – Delivers the negotiated node address
- j1939.DeleteMessageFilter – Deletes a message filter
Overall, the sketch supports three versions of the ARD1939 protocol stack, two for the Arduino Uno (ARD1939-Uno and ARD1939-Uno/TP) and one for the Arduino Mega 2560 (ARD1939-Mega).
- ARD1939-Uno – For the sake of saving memory resources, this version does not support the Transport Protocol (TP) according to SAE J1939/21.
Program Storage Space: ~8,200 bytes (~25%)
Dynamic Memory: ~510 bytes (~25%)
Message Filters: 10
- ARD1939-Uno/TP – This version supports the Transport Protocol (TP) but only for message lengths up to 256 bytes.
Program Storage Space: ~12,000 bytes (~37%)
Dynamic Memory: ~1,100 bytes (~55%)
Message Filters: 10
- ARD1939-Mega – This version supports the full SAE J1939 protocol stack including Transport Protocol (TP) for message lengths up to 1785 bytes according to the J1939 standard.
Program Storage Space: ~12,300 bytes (~4%)
Dynamic Memory: ~4,600 bytes (~56%)
Note: The exact program storage space and dynamic memory requirements also depend on the extend of the actual J1939 application in the loop () function.
The functionality settings can be found inside the ARD1939.h file, which is part of the project.
Function Calls Description
void j1939.Init(int nSystemTime);
Initializes the ARD1939’s memory, baud rate settings, etc.
nSystemTime - This is the loop time of your application in milliseconds. This information will provide the ARD1939 protocol stack a time base to manage all timers required for various protocol tasks.
Ideally, the system time should be 1 millisecond for best performance, however, up to 10 milliseconds should be sufficient for regular network traffic. Any higher values can be used but may jeopardize performance.
void j1939.SetPreferredAddress(byte nAddr);
Sets the preferred node address. Default setting is 128 (see ARD1939.h file). This function call is mandatory for initializing the protocol stack; otherwise the stack will not be able to send messages into the vehicle network. The preferred address is independent of the negotiable address range, i.e. it can be set anywhere within or outside that range.
nAddr – This is the preferred node (source) address. It should be in a range between 128 and 252. Addresses lower than 128 are allowed, but are regulated by the SAE J1939 Standard.
void j1939.SetAddressRange(byte nAddrBottom, byte nAddrTop);
Sets the negotiable address range. The default range (per ARD1939.h file) is 129 to 247. This function call is optional, meaning the protocol stack will work only with the preferred address.
nAddrBottom defines the bottom of the negotiable address range.
nAddrTop defines the top of the negotiable address range.
void j1939.SetNAME(long lIdentityNumber, int nManufacturerCode, byte nFunctionInstance, byte nECUInstance, byte nFunction, byte nVehicleSystem, byte nVehicleSystemInstance, byte nIndustryGroup, byte nArbitraryAddressCapable);
Sets the ECU’s NAME by using individual parameters.
The following shows the default settings for the device’s NAME as found in the ARD1939.h file:
#define NAME_IDENTITY_NUMBER 0xFFFFFF
#define NAME_MANUFACTURER_CODE 0xFFF
#define NAME_FUNCTION_INSTANCE 0
#define NAME_ECU_INSTANCE 0x00
#define NAME_FUNCTION 0xFF
#define NAME_RESERVED 0
#define NAME_VEHICLE_SYSTEM 0x7F
#define NAME_VEHICLE_SYSTEM_INSTANCE 0
#define NAME_INDUSTRY_GROUP 0x00
#define NAME_ARBITRARY_ADDRESS_CAPABLE 0x01
nArbitraryAddressCapable should be set to zero when your application does not support a negotiable address range (see function j1939SetAddressRange).
The NAME fields have been assigned in a way that they will not interfere when used within an existing vehicle network. This has been done by setting the Identity Number and Manufacturer Code to the maximum value, which will result in a more passive role during the address claim process. An ECU with a NAME of higher value is more likely to lose the competition with another node using the same address.
Note: All settings as shown are used for demonstration purposes only. In all consequence, you must follow the SAE's recommendations. Also, you alone (and not the author or publisher) are responsible for the final implementation and the results thereof.
byte j1939.SetMessageFilter(long lPGN);
Sets the PGNs to be processed in your application. ARD1939 supports up to 10 (UNO) or 100 (Mega 2560) message filters.
lPGN – This is the PGN you allow to be passed to your application.
Function returns OK or ERROR (as defined in ARD1939.h) where EROR means that no more message filters are available.
Special case – Request Message:
As defined in the SAE J1939 Standard, the Request message is 0xEA00, where the LSB is used as the destination address, i.e. the address of the ECU that is supposed to provide the requested information (e.g. 0xEA80, where 0x80 is the destination address).
The ARD1939 protocol stack’s message filter, however, will allow every message in the 0xEAxx range to pass when you set any filter PGN in the 0xEAxx range.
This behavior is necessary to allow the global address (255), meaning there are scenarios where one ECU requests the information from all nodes in the network.
As a consequence, it is mandatory that the application, in addition to the Request message, also verifies the destination address with its own address.
byte j1939.Operate(byte* nMsgId, long* lPGN, byte* pMsg, int* nMsgLen, byte* nDestAddr, byte* nSrcAddr, byte* nPriority);
Handles the address claim process, reads PGNs from the vehicle network, and delivers the current protocol status (Address Claim in progress, Address Claim successful, Address Claim failed).
The function returns ADDRESSCLAIM_INPROGRESS, NORMALDATATRAFFIC (Address claim successful), or ADDRESSCLAIM_FAILED.
The parameters passed to the function are pointers to:
nMsgId = J1939_MSG_NONE – No message received or J1939_MSG_MSG_APP – Message was received.
lPGN = PGN of received message
pMsg = Message data array
nMsgLen = Size of message data array
nDestAddr = The message’s destination address (usually the source address of your application but could also be the global address 255 – message broadcasting)
nSrcAddr = The source address, i.e. the address of the node who sent the message.
nPriority = Message priority.
byte j1939.Transmit(byte nPriority, long lPGN, byte nSourceAddress, byte nDestAddress, byte* pData, int nDataLen);
Transmits data to the vehicle network and handles the Transport Protocol (TP), meaning it handles data messages between 0 and 1785 bytes long (For the Arduino Uno, this number is limited to 256). The function automatically invokes the Transport Protocol (TP) when the message is longer than 8 bytes.
The parameters passed to the function are:
lPGN = PGN of the message
pMsg = Message data array
nMsgLen = Size of message data array
nDestAddr = The message’s destination address (could also be the global address 255 for message broadcasting)
nSrcAddr = The source address, i.e. the address of your ECU.
nPriority = Message priority.
Resets the protocol stack settings.
Delivers the negotiated node address; will be NULLADDRESS (254) in case the address claim process failed.
void j1939.DeleteMessageFilter(long lPGN);
Deletes a message filter.
lPGN = PGN to be deleted. Any attempts to delete a PGN that has not been set previously, will be ignored.
A Comprehensible Guide to J1939
SAE J1939 has become the accepted industry standard and the vehicle network technology of choice for off-highway machines in applications such as construction, material handling, and forestry machines. J1939 is a higher-layer protocol based on Controller Area Network (CAN). It provides serial data communications between microprocessor systems (also called Electronic Control Units - ECU) in any kind of heavy duty vehicles. The messages exchanged between these units can be data such as vehicle road speed, torque control message from the transmission to the engine, oil temperature, and many more.
The information in this book is based on two documents of the SAE J1939 Standards Collection: J1939/21 - Data Link Layer J1939/81 - Network Management A Comprehensible Guide to J1939 is the first work on J1939 besides the SAE J1939 standards collection. It provides profound information on the J1939 message format and network management combined with a high level of readability.