DanielMartensson / Open-SAE-J1939

SAE J1939 protocol free to use for embedded systems or PC with CAN-bus
MIT License
455 stars 164 forks source link
arduino avr c canbus embedded j1939 pic sae stm32

Open SAE J1939

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++ GUI framework that uses Open SAE J1939 over the USB? https://github.com/DanielMartensson/GoobySoft

Looking for a C STM32 project with Open SAE J1939? https://github.com/DanielMartensson/STM32-PLC

Getting started

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 select PROCESSOR_CHOICE 0 and run some examples. That's the debugging mode for internal CAN feedback.

How to use the project

/*
 * Main.c
 *
 *  Created on: 16 juli 2021
 *      Author: Daniel Mårtensson
 */

#include <stdio.h>

 /* 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 < DLC; i++) {
        printf("%X\t", data[i]);
    }

    /* Print the non-data */
    for (i = DLC; i < 8U; i++) {
        printf("%X\t", 0U);
    }

    /* New line */
    printf("\n");
}

/* Apply your delay here */
void Callback_Function_Delay(uint8_t delay){
    /* Place your hardware delay here e.g HAL_Delay(delay); for STM32 */
}

int main() {

    /* Create our J1939 structure */
    J1939 j1939 = { 0 };

    /*
     * Callbacks can be used if you want to pass a specific CAN-function into the hardware layer.
     * All you need to do is to enable INTERNAL_CALLLBACK inside hardware.h
     * If you don't want to have the traffic callback, just set the argument as NULL.
     * If you don't want any callback at all, you can write your own hardware layer by selecting a specific processor choice at hardware.h
     */
    CAN_Set_Callback_Functions(Callback_Function_Send, Callback_Function_Read, Callback_Function_Traffic, Callback_Function_Delay);

    /* Load your ECU information */
    Open_SAE_J1939_Startup_ECU(&j1939);

    /* SAE J1939 process */
    bool run = true;
    while (run) {
        /* Read incoming messages */
        Open_SAE_J1939_Listen_For_Messages(&j1939);

        /* Your application code here */

    }

    /* Save your ECU information */
    Open_SAE_J1939_Closedown_ECU(&j1939);

    return 0;
}

See the examples in Examples -> SAE J1939 how to change the address, NAME or identifications for your ECU.

The structure of the project

a

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.

SAE J1939 functionality

Extra functionality

Questions and answers