Open SAE J1939 is a free C implementation of a protocol you normally pay for
SAE J1939 protocol free to use for embedded systems or PC with CAN-bus
At a glance
- What is it?
- Open SAE J1939 is a C library for shaping CAN bus messages to the SAE J1939 standard used by tractors, trucks and machinery, written in ANSI C without dynamic allocation so it can sit under MISRA C rules. The trade is that the documentation is a PDF in the repository rather than a reference, and the project covers only part of a very large standard.
- Who is it for?
- Open SAE J1939 suits an embedded developer who needs to speak SAE J1939 on a tractor, truck or machine controller and cannot buy the standard, and who is comfortable reading a PDF and editing a header to select the target processor.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap is the price of the specification, not the complexity of the protocol
The problem this project addresses is an information problem rather than a technical one. The README says SAE J1939 is a protocol for shaping CAN bus messages in a specific way that suits industrial vehicles such as tractors, machinery and trucks, and that it is a very easy protocol to use. The difficulty is access: the author writes that there is a lack of information about SAE J1939 because of the cost of the protocol document. So the library exists to fill a gap that is mostly about documentation and reference code rather than about hard engineering. The intended user is an embedded developer working on a controller that has to talk to existing vehicle equipment, and the topic tags name the expected hardware directly, with arduino, avr, c, canbus, embedded, pic, sae and stm32. The repository also points at a sibling CANopen library and an STM32 PLC project, and at a separate GUI logging tool for analysing CANopen and J1939 messages.
ANSI C and no dynamic allocation, so MISRA C stays reachable
The language choices are stated as deliberate constraints rather than accidents. The README says the project is written in C because C is an industry standard, that the chosen dialect is ANSI C (C89), and that dynamic memory allocation is not used in the library. The consequence the author draws is that it will work with the MISRA C standard, which is the standard automotive and safety software projects write against. The CMake section repeats the same philosophy in a different form, saying the project follows an embedded-target philosophy where memory is statically allocated at startup and does not grow dynamically at runtime. That is a real constraint on what you can build on top of it, and it is also what makes the library usable on a microcontroller where the heap is a design decision you make once. For a project without a safety argument, the static allocation is a limitation; for one with it, it is the reason the code can be reviewed.
The hardware is selected by editing a header, not a config file
The processor choice is the first thing you do, and the mechanism is a source edit rather than a build option. The README says to select the processor choice in the Hardware -> Hardware.h file, where you can pick STM32, Arduino, PIC or AVR, and that if you want to run it on a PC first you build with the define OPENSAE_J1939_TARGET_PLATFORM and run some examples, which it calls the debugging mode for internal CAN feedback. That last point matters for anyone starting: the default is that all CAN messages are sent and received locally over an internal memory bus, so you can develop the protocol logic on a PC with no hardware at all, then switch to the real target. The consequence of the mechanism is that switching platforms means changing a header, and the README asks you to write the code for a processor that is not there and send a pull request.
CMake builds, and the target platform is a compile definition
The build is CMake, and the whole build sequence is a handful of commands, from a clone to a compiled binary:
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 build@@C1@@The one non-obvious flag is OPENSAE_J1939_TARGET_PLATFORM, set to SOCKETCAN in the example. Per default, no specific hardware platform is selected and all CAN messages are sent and received locally over an internal memory bus, so the SOCKETCAN value is what points the library at a real SocketCAN interface on a Linux host. The README also says the build can be configured to target different hardware platforms and invites contributions for other targets, which tells you the platform list is a work in progress rather than a fixed set.
Consuming it as a library, two ways
The library is named opensaej1939 in CMake, and the README gives two ways to link it. The first uses FetchContent to pull the repository at a given git tag straight into your build, so no submodule is needed:
# 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)@@C2@@The alternative is the traditional submodule route, where the project is added with add_subdirectory and then the same target_link_libraries call links your executable against opensaej1939. Either way the last line of the snippet is the part that matters for your project, since it is what puts the J1939 stack under your own main.c. The README is explicit that the library is for embedded systems or a PC with a CAN bus, and the CMake section repeats that the build can also be configurable with more compile time definitions that tune the inner workings of the stack.
Proprietary A and B data is sized at build time, not at runtime
The one configuration area the README goes into detail on is data transfer, and it follows directly from the no-allocation rule. The author explains the consequence: the project does not want to allocate more memory than necessary, so the number of data transfer PGNs and their data sizes are configured. Three definitions control this. MAX_PROPRIETARY_A is the maximum size in bytes of the Proprietary A data, MAX_PROPRIETARY_B is the maximum supported size in bytes of a single Proprietary B data unit, and MAX_PROPRIETARY_B_PGNS is the maximum number of distinct Proprietary B PGNs that can be handled. If they are not set during the CMake setup stage they take default values, with MAX_PROPRIETARY_A at 15U, MAX_PROPRIETARY_B at 60 and MAX_PROPRIETARY_B_PGNS at 2. The README then gives a command that sets the SocketCAN backend and raises the Proprietary B PGN limit to 124, which is a realistic example of a vehicle with more proprietary messages than the default allows.
cmake -B build -DOPENSAE_J1939_TARGET_PLATFORM=SOCKETCAN -DMAX_PROPRIETARY_B_PGNS=124 .The learning curve is a PDF, and the standard is only partly implemented
The getting started section is unusual and worth being direct about: the first instruction is to read the author's own PDF document of Open SAE J1939 inside the Documentation folder, because the README says you need to learn the structure of the project or you will not be able to understand SAE J1939. So the project ships a document rather than a tutorial, and that document is part of the repository rather than a link to a website. The scope limitation is stated just as plainly. The author says he has built up a basic structure of the project and hopes other users will send pull requests with their C code for extra functionality, because SAE J1939 is a huge standard. The four usage steps then follow: download the repository, select the processor in Hardware.h, copy the Src folder into your project and rename it, and start from the Examples -> Open SAE J1939 -> Main.txt example. The repository also ships an ECUINFO.TXT at the root and a tests/ directory.
Editorial conclusion
Open SAE J1939 suits an embedded developer who needs to speak SAE J1939 on a tractor, truck or machine controller and cannot buy the standard, and who is comfortable reading a PDF and editing a header to select the target processor. It does not suit a project that needs full J1939 conformance, since the author himself describes the standard as huge and asks for pull requests to extend it, and it does not suit a team that wants a maintained CAN stack for a safety-critical machine without reviewing every line. Before adopting, read the Open SAE J1939 PDF in the Documentation folder, set the processor in Hardware.h, and note that the last push to the repository was on 2026-10-17 with no GitHub releases published.
Frequently asked questions
How do I build Open SAE J1939 with CMake?
Clone the repository, change into the directory, create a build directory and run `cmake -B build -DOPENSAE_J1939_TARGET_PLATFORM=SOCKETCAN .` followed by `cmake --build build`. Per default no specific hardware platform is selected and all CAN messages are sent and received locally over an internal memory bus.
How do I use Open SAE J1939 in my own CMake project?
Either use FetchContent to declare opensaej1939 with a GIT_REPOSITORY and GIT_TAG and then call FetchContent_MakeAvailable, or add the project as a Git submodule and use add_subdirectory. In both cases you link your executable with `target_link_libraries(your_executable PRIVATE opensaej1939)`.
Which processors does Open SAE J1939 support?
You select the processor in the Hardware -> Hardware.h file, with STM32, Arduino, PIC and AVR named as examples. If your processor is not there, the README asks you to write the code for it and send a pull request. You can also build with the OPENSAE_J1939_TARGET_PLATFORM define and run on a PC for internal CAN feedback.
Why does Open SAE J1939 not allocate memory dynamically?
The README states the library uses no dynamic memory allocation and that the project follows an embedded-target philosophy where memory is statically allocated at startup and does not grow at runtime, so the code can work with the MISRA C standard. The consequence is that the number of data transfer PGNs and their sizes are set with compile time definitions such as MAX_PROPRIETARY_A, MAX_PROPRIETARY_B and MAX_PROPRIETARY_B_PGNS.
Is Open SAE J1939 a complete implementation of the standard?
No. The README says the author has built up a basic structure of the project and hopes that other users will send pull requests with their C code for extra functionality, because SAE J1939 is a huge standard. The first thing the README tells you to do is read the Open SAE J1939 PDF in the Documentation folder to learn the structure of the project.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/danielmartensson-open-sae-j1939)Was this useful?