SAE J1939 protocol free to use for embedded systems or PC with CAN-bus
SAE J1939 protocol free to use for embedded systems or PC with CAN-bus
SAE J1939 is a protocol for shaping the CAN-bus message in a specific way that suits industrial vehicles such as tractors, machinery, trucks and more.
SAE J1939 is a very easy protocol to use, but there is a lack of information about SAE J1939, due to the cost of the protocol document, available how to shape a CAN-bus message according to SAE J1939 protocol standard. So therefore I’m writing a SAE J1939 protocol available for free to use on any embedded systems such as STM32, Arduino, AVR, PIC etc or PC.
To learn to build on this project, you need first to understand SAE J1939. I have written this project in C language because C is an industry standard. The C language dialect I have chosen is ANSI C (C89) and I don't use dynamical memory allocation in this library. So it will work with MISRA C standard.
With this library, you can communicate with valves, engines, actuators, machinery, hardware and all other things that are suitable for heavy industrial mobile applications. I have build up a basic structure of the project and I hope that other users will send pull request of their C code for extra functionality to SAE J1939 standard because SAE J1939 is a huge standard.
Looking for C CANopen library for embedded systems? https://github.com/DanielMartensson/Easy-CANopen
Looking for a C STM32 project with Open SAE J1939? https://github.com/DanielMartensson/STM32-PLC
There is a GUI logging software that can analyze CANopen messages ad even SAE J1939 messages as well. Head over to GoobySoft
The first thing you need to know is to read my own PDF document of Open SAE J1939 inside the Documentation folder.
Learn the structure of the project, else you won't be able to understand SAE J1939.
After you have got a basic understanding of the project, you are able to build on it. Keep it simple and follow the
SAE J1939 standard!
After you have understand the structure of the project, then select processor choice in Hardware -> Hardware.h file.
Here you can select for example STM32, Arduino, PIC, AVR etc. or if you want to run it on PC first, then build with the define OPENSAE_J1939_TARGET_PLATFORM and run some examples.
That's the debugging mode for internal CAN feedback.
This project can be built with CMake. The build can be configured to target different hardware platforms.
Per default, no specific hardware platform is selected and all CAN messages are sent and received locally
over an internal memory bus. However, if you want to send messages on a specific platform, you can set the
OPENSAE_J1939_TARGET_PLATFORM compile definition during build. Here is an example of how to build the project using
SOCKETCAN as a targeted platform.
git clone https://github.com/DanielMartensson/Open-SAE-J1939.git
cd Open-SAE-J1939
mkdir build
cmake -B build -DOPENSAE_J1939_TARGET_PLATFORM=SOCKETCAN .
cmake --build buildPlease feel free to contribute to this project if you add platform support for another target.
In CMake the library is named opensaej1939. To link it with your executable use it with the target_link_libraries command, as an example:
# Fetch all dependencies from git
include(FetchContent)
# Fetch Open-SAE-J1939
FetchContent_Declare(
opensaej1939
GIT_REPOSITORY https://github.com/DanielMartensson/Open-SAE-J1939.git
GIT_TAG main
)
# This line actually downloads and adds the project
FetchContent_MakeAvailable(opensaej1939)
# Your executable
add_executable(your_executable main.c)
# Link against the Open SAE J1939 library
target_link_libraries(your_executable PRIVATE opensaej1939)If you instead work with Git submodules in your repository, you can include the CMakeLists.txt from this project using the add_subdirectory command like this:
# Add the Open-SAE-J1939 submodule as a subdirectory
add_subdirectory(external/Open-SAE-J1939)
# Your executable
add_executable(your_executable main.c)
# Link against the Open SAE J1939 library
target_link_libraries(your_executable PRIVATE opensaej1939)The build can also be configurable. You can set the target platform with the OPENSAE_J1939_TARGET_PLATFORM define, as established above. You can also set some more detailed behaviour of the inner workings of the stack with compile time definitions. The project follows an embedded-target philosophy, where memory is statically allocated at startup and does not grow dynamically at runtime. This has a consequence when working with data transfer support. We don't want to allocate more memory than necessary. You can configure the number of data transfer PGNS as well as their data sizes accordingly:
| Definition | Description |
|---|---|
MAX_PROPRIETARY_A |
The maximum size (in bytes) of the Proprietary A data. |
MAX_PROPRIETARY_B |
The maximum supported size (in bytes) of a single Proprietary B data unit. |
MAX_PROPRIETARY_B_PGNS |
The maximum number of distinct Proprietary B PGNs that can be handled. |
If not specifically set during the cmake build setup stage, they will attain these default values:
| Definition | Default Value |
|---|---|
MAX_PROPRIETARY_A |
15U |
MAX_PROPRIETARY_B |
60 |
MAX_PROPRIETARY_B_PGNS |
2 |
As an example, this command would set up your project to use the SocketCAN backend as well as setting the maximum number of distinct Proprietary B PGNs to 124 for your project.
cmake -B build -DOPENSAE_J1939_TARGET_PLATFORM=SOCKETCAN -DMAX_PROPRIETARY_B_PGNS=124 .Hardware -> Hardware.h and select your processor, if it's not available, please write code for it and send me a pull requestSrc folder to your project folder inside your IDE. Rename Src to for example Open SAE J1939. That's a good name.Examples -> Open SAE J1939 -> Main.txt example as your initial starting code for a SAE J1939 project./*
* Main.c
*
* Created on: 16 juli 2021
* Author: Daniel Mårtensson
*/
#include
/* Include Open SAE J1939 */
#include "Open_SAE_J1939/Open_SAE_J1939.h"
/* Include ISO 11783 */
#include "ISO_11783/ISO_11783-7_Application_Layer/Application_Layer.h"
void Callback_Function_Send(uint32_t ID, uint8_t DLC, uint8_t data[]) {
/* Apply your transmit layer here, e.g:
* uint32_t TxMailbox;
* static CAN_HandleTypeDef can_handler;
* This function transmit ID, DLC and data[] as the CAN-message.
* HardWareLayerCAN_TX(&can_handler, ID, DLC, data, &TxMailbox);
*
* You can use TCP/IP, USB, CAN etc. as hardware layers for SAE J1939
*/
}
void Callback_Function_Read(uint32_t* ID, uint8_t data[], bool* is_new_data) {
/* Apply your receive layer here, e.g:
* CAN_RxHeaderTypeDef rxHeader = {0};
* static CAN_HandleTypeDef can_handler;
* This function read CAN RX and give the data to ID and data[] as the CAN-message.
* if (HardWareLayerCAN_RX(can_handler, &rxHeader, ID, data) == STATUS_OK){
* *is_new_data = true;
* }
*
* You can use TCP/IP, USB, CAN etc. as hardware layers for SAE J1939
*/
}
/* This function reads the CAN traffic */
void Callback_Function_Traffic(uint32_t ID, uint8_t DLC, uint8_t data[], bool is_TX) {
/* Print if it is TX or RX */
printf("%s\t", is_TX ? "TX" : "RX");
/* Print ID as hex */
printf("%08X\t", ID);
/* Print the data */
uint8_t i;
for (i = 0U; i SAE J1939` how to change the address, NAME or identifications for your ECU.
# The structure of the project
# A Working example how the structure of Open SAE J1939 is done
This flow chart in code how Open SAE J1939 library is working. This example demonstrates how to send a request and get an answer.
- Step 1: `ECU X` is going to send a `PGN` to `ECU Y`. Interpret `PGN` as a function code.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/4297cff44107e5278f120243cb9a611eafe8c42f/Src/SAE_J1939/SAE_J1939-71_Application_Layer/Request_ECU_Identification.c#L18-L20
- Step 2: `ECU Y` is going to read that `PGN` message from `ECU X`.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/4297cff44107e5278f120243cb9a611eafe8c42f/Src/Open_SAE_J1939/Listen_For_Messages.c#L18-L32
- Step 3: The `PGN` function code will be interpreted by `ECU Y`.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/678473c13cb7eb5fe46d5aac30a53efad4ccefd9/Src/SAE_J1939/SAE_J1939-21_Transport_Layer/Request.c#L20-L52
- Step 4: The `PGN` function code is now interpreted as `ECU Identification` by `ECU Y`. Then `ECU Y` is going to broadcast the `ECU Identification` to all `ECUs`.
- Step 4.1.1: For 1 package message, `ECU Y` is going to broadcast the `ECU Identification`.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/678473c13cb7eb5fe46d5aac30a53efad4ccefd9/Src/SAE_J1939/SAE_J1939-71_Application_Layer/Request_ECU_Identification.c#L26-L42
- Step 4.1.2: `ECU X` read the response from `ECU Y` because the `ECU Identification` is broadcasted.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/8faf1a542b291cb2ff71f20fceb941bb6113127b/Src/Open_SAE_J1939/Listen_For_Messages.c#L61-L62
- Step 4.2.1: For Multi Package Message, the control byte can either be BAM or RTS. BAM is only used if you send to all `ECUs` e.g address `0xFF = 255`. But if the control byte is RTS, e.g address is not `0xFF`, then `ECU Y` is going to send a RTS and listen for a CTS response by `ECU X`. RTS is a question for "Let me know when I can transmit the message?" and CTS is response `Now you can transmit the message to me`.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/678473c13cb7eb5fe46d5aac30a53efad4ccefd9/Src/SAE_J1939/SAE_J1939-71_Application_Layer/Request_ECU_Identification.c#L44-L58
- Step 4.2.2: If `ECU Y` is sending a RTS, then `ECU X` will read the RTS and response with CTS back to `ECU Y`
https://github.com/DanielMartensson/Open-SAE-J1939/blob/678473c13cb7eb5fe46d5aac30a53efad4ccefd9/Src/SAE_J1939/SAE_J1939-21_Transport_Layer/Transport_Protocol_Connection_Management.c#L22-L26
- Step 4.2.3: Once `ECU Y` has received the CTS, then it going to transmit the data, in this case `ECU Identification`, back to `ECU X`.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/678473c13cb7eb5fe46d5aac30a53efad4ccefd9/Src/SAE_J1939/SAE_J1939-21_Transport_Layer/Transport_Protocol_Connection_Management.c#L29-L31
- Step 4.2.3: Once `ECU Y` is sending package after package...
https://github.com/DanielMartensson/Open-SAE-J1939/blob/8faf1a542b291cb2ff71f20fceb941bb6113127b/Src/SAE_J1939/SAE_J1939-21_Transport_Layer/Transport_Protocol_Data_Transfer.c#L84-L105
- Step 4.2.4: Then `ECU X` is recieving each package and building up the message by knowing the `PNG` function code.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/8faf1a542b291cb2ff71f20fceb941bb6113127b/Src/SAE_J1939/SAE_J1939-21_Transport_Layer/Transport_Protocol_Data_Transfer.c#L19-L48
And then finally implement the message.
https://github.com/DanielMartensson/Open-SAE-J1939/blob/8faf1a542b291cb2ff71f20fceb941bb6113127b/Src/SAE_J1939/SAE_J1939-21_Transport_Layer/Transport_Protocol_Data_Transfer.c#L66-L68
# SAE J1939 functionality
- SAE J1939:21 Transport Layer
- Acknowledgement
- Request
- Transport Protocol Connection Management with BAM, CTS, RTS and EOM
- Transport Protocol Data Transfer
- SAE J1939:71 Application Layer
- Request Component IdentificaNo open issues yet, or sync has not completed.