Kvaser J1939 Library
tp_sender.cpp
/*
* Copyright 2026 by KVASER AB, SWEDEN
*
* WWW: https://www.kvaser.com
*
* This software is furnished under a license and may be used and copied
* only in accordance with the terms of such license.
*
*/
#include <kvj1939lib.h>
#include <chrono>
#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
// J1939 null address constant
static constexpr uint8_t J1939_NULL_ADDRESS = 254;
// J1939 channel class that wraps the J1939 C API.
//
// This class manages the lifetime of the channel handle and supports
// registering C++ lambda callbacks for channel events.
class J1939Channel {
private:
J1939Channel() { }
// Delete copy and move operations to prevent accidental handle duplication
J1939Channel(const J1939Channel&) = delete;
J1939Channel& operator=(const J1939Channel&) = delete;
J1939Channel(J1939Channel&&) = delete;
J1939Channel& operator=(J1939Channel&&) = delete;
public:
// Define C++ callback signatures
using OnAddressClaimedParams
= std::function<void(uint8_t address, bool valid)>;
using OnTxCompleteParams
= std::function<void(uint32_t request_handle, bool success)>;
~J1939Channel()
{
if (_channel_handle != 0) {
// Destroy (and close) channel when object is destroyed
j1939_destroy_channel(_channel_handle);
}
}
// Create a channel
static std::unique_ptr<J1939Channel> create(int can_channel_index,
int bitrate)
{
std::unique_ptr<J1939Channel> channel(new J1939Channel());
params.channel = can_channel_index;
params.support_virtual = true;
params.bit_rate = bitrate;
if (result.result != J1939_STATUS_OK) {
std::cerr << "Failed to create channel. Error code: "
<< result.result << std::endl;
return nullptr;
}
channel->_channel_handle = result.hnd;
= j1939_register_callbacks(channel->_channel_handle, channel.get(),
nullptr, tx_cb_wrapper, ac_cb_wrapper);
if (res != J1939_STATUS_OK) {
std::cerr << "Failed to register callbacks. Error code: " << res
<< std::endl;
return nullptr;
}
return channel;
}
// Open channel
bool open()
{
if (_is_open) {
return true;
}
J1939Result res = j1939_open_channel(_channel_handle);
if (res != J1939_STATUS_OK) {
std::cerr << "Failed to open channel. Error code: " << res
<< std::endl;
return false;
}
_is_open = true;
if (_address != J1939_NULL_ADDRESS) {
j1939_claim_address(_channel_handle, _address, _name);
}
return true;
}
// Close channel
bool close()
{
if (!_is_open) {
return true;
}
J1939Result res = j1939_close_channel(_channel_handle);
if (res != J1939_STATUS_OK) {
std::cerr << "Failed to close channel. Error code: " << res
<< std::endl;
return false;
}
_is_open = false;
return true;
}
// Set address to be claimed
void claim_address(uint8_t address, uint64_t name)
{
_address = address;
_name = name;
if (_is_open) {
j1939_claim_address(_channel_handle, _address, _name);
}
}
// Send a message from the claimed address
uint32_t send_message(uint32_t pgn, uint8_t dest, uint8_t prio,
const std::vector<uint8_t>& data)
{
params.id.pgn = pgn;
params.id.priority = prio;
params.id.src_addr = _address;
params.id.dest_addr = dest;
params.payload = data.data();
params.payload_length = (uint32_t)data.size();
params.request_handle = ++_tx_handle_counter;
J1939Result res = j1939_write(_channel_handle, params);
if (res != J1939_STATUS_OK) {
std::cerr << "j1939_write failed: " << res << std::endl;
return 0;
}
// Return the request handle
return params.request_handle;
}
// Register user-provided lambda hooks
void register_on_address_claimed(OnAddressClaimedParams cb)
{
_ac_callback = cb;
}
void register_on_tx_complete(OnTxCompleteParams cb) { _tx_callback = cb; }
private:
J1939ChannelHandle _channel_handle = 0;
bool _is_open = false;
uint32_t _tx_handle_counter = 0;
uint8_t _address = J1939_NULL_ADDRESS;
uint64_t _name = 0;
OnAddressClaimedParams _ac_callback;
OnTxCompleteParams _tx_callback;
// Static callback wrappers
static void tx_cb_wrapper(J1939ChannelHandle channel, void* context,
uint32_t request_handle, J1939Identifier id,
uint64_t timestamp, bool error)
{
auto* ch = static_cast<J1939Channel*>(context);
if (ch && ch->_tx_callback) {
ch->_tx_callback(request_handle, !error);
}
}
static void ac_cb_wrapper(J1939ChannelHandle channel, void* context,
uint8_t address, bool valid)
{
auto* ch = static_cast<J1939Channel*>(context);
if (ch && ch->_ac_callback) {
ch->_ac_callback(address, valid);
}
}
};
// Prints out the help message for the command line options
void print_help()
{
printf(
"Usage: tp_sender [options]\n"
"Options:\n"
" --channel <channel>, -c <channel> Set CAN channel (default: 0)\n"
" --bit-rate <bit_rate>, -b <bit_rate> Set CAN bit rate (default: "
"250000)\n"
" --help, -h Show this help message\n");
}
// Example application demonstrating address claiming and sending a multi-frame
// J1939 message using the Kvaser J1939 library.
//
// This function creates a J1939 channel, claims address 0x80, and sends a
// multi-frame BAM message once the address claim is successful. C++ lambda
// functions are used to handle channel events asynchronously.
int main(int argc, char** argv)
{
unsigned int channel_no = 0;
unsigned int bit_rate = 250000;
// Parse command line arguments
for (int i = 1; i < argc; ++i) {
if ((strcmp(argv[i], "--channel") == 0 || strcmp(argv[i], "-c") == 0)
&& i + 1 < argc) {
channel_no = (unsigned int)strtoul(argv[++i], NULL, 0);
} else if ((strcmp(argv[i], "--bit-rate") == 0
|| strcmp(argv[i], "-b") == 0)
&& i + 1 < argc) {
bit_rate = (unsigned int)strtoul(argv[++i], NULL, 0);
} else {
print_help();
return 0;
}
}
auto channel = J1939Channel::create(channel_no, bit_rate);
if (!channel) {
std::cerr << "Failed to create J1939 channel." << std::endl;
return 1;
}
// Set address which will be claimed when channel is opened
channel->claim_address(0x80, 0x123456789ABCDEF0);
// Prepare message
struct message_t {
uint32_t pgn;
uint8_t dest;
std::vector<uint8_t> data;
} message = {
0x00F400,
0xFF,
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 },
};
uint32_t my_tx_handle = 0;
// Address claim callback
// Will send the message when address claim is completed
channel->register_on_address_claimed([&](uint8_t address, bool valid) {
if (valid) {
std::cout << "[Callback] Address " << (int)address
<< " claimed successfully." << std::endl;
my_tx_handle = channel->send_message(message.pgn, message.dest, 7,
message.data);
} else {
std::cout << "[Callback] Address claim failed for " << (int)address
<< "." << std::endl;
std::exit(1);
}
});
// TX Complete callback
// Will exit the application when our message has been sent
channel->register_on_tx_complete([&](uint32_t tx_handle, bool success) {
if (tx_handle == my_tx_handle) {
if (success) {
std::cout << "[Callback] Message successfully sent!"
<< std::endl;
std::exit(0);
} else {
std::cout << "[Callback] Failed to send message!" << std::endl;
std::exit(1);
}
}
});
// Open channel (will trigger address claim)
std::cout << "Opening J1939 channel..." << std::endl;
if (!channel->open()) {
std::cerr << "Failed to open J1939 channel." << std::endl;
return 1;
}
std::cout << "Waiting for events..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
std::cerr << "Timeout while waiting for events." << std::endl;
return 1;
}
Main API for the KVJ1939 Library.
J1939Result j1939_write(J1939ChannelHandle hnd, const J1939WriteParams message)
Send a J1939 message.
J1939Result j1939_claim_address(J1939ChannelHandle hnd, uint8_t address, uint64_t name)
Claim a J1939 address on the bus.
J1939ChannelResult j1939_create_channel(const J1939ChannelParams params)
Create a J1939 channel.
J1939Result j1939_destroy_channel(J1939ChannelHandle hnd)
Destroy a J1939 channel.
J1939Result j1939_register_callbacks(J1939ChannelHandle hnd, void *context, J1939RxCallback rx_callback, J1939TxCallback tx_callback, J1939AcCallback ac_callback)
Set callbacks for a J1939 channel.
J1939Result j1939_close_channel(J1939ChannelHandle hnd)
Close a J1939 channel and go off bus.
J1939Result j1939_open_channel(J1939ChannelHandle hnd)
Open a J1939 channel and go on bus.
uint32_t J1939ChannelHandle
A handle to a J1939 channel.
Definition: kvj1939lib_types.h:72
J1939Result
Return status codes, indicating the result of an operation.
Definition: kvj1939lib_types.h:16
@ J1939_STATUS_OK
Operation completed successfully.
Definition: kvj1939lib_types.h:18
A struct that holds the parameters for creating a j1939 channel.
Definition: kvj1939lib_types.h:77
uint32_t channel
The CANlib channel number to use.
Definition: kvj1939lib_types.h:79
uint32_t bit_rate
The bit rate to use for the channel. If set to 0, then this channel will be opened with NO_INIT_ACCES...
Definition: kvj1939lib_types.h:84
bool support_virtual
True if virtual channels are supported.
Definition: kvj1939lib_types.h:81
A struct that holds the result and handle for creating a j1939 channel.
Definition: kvj1939lib_types.h:91
J1939Result result
The result of the operation.
Definition: kvj1939lib_types.h:93
J1939ChannelHandle hnd
The handle to the created channel, if successful.
Definition: kvj1939lib_types.h:95
A struct that represents a J1939 identifier.
Definition: kvj1939lib_types.h:101
uint8_t priority
The priority of the message. 0 = highest, 7 = lowest.
Definition: kvj1939lib_types.h:105
uint8_t dest_addr
The destination address of the message. Ignored for PDU2 messages.
Definition: kvj1939lib_types.h:109
uint8_t src_addr
The source address of the message.
Definition: kvj1939lib_types.h:107
uint32_t pgn
The message PGN. The lower byte is 0 for PDU1 messages.
Definition: kvj1939lib_types.h:103
A struct that holds the parameters for a J1939 write request.
Definition: kvj1939lib_types.h:115
const uint8_t * payload
Pointer to the message data to be sent.
Definition: kvj1939lib_types.h:119
struct J1939Identifier id
The J1939 identifier of the message to be sent.
Definition: kvj1939lib_types.h:117
uint32_t payload_length
The length of the message data to be sent.
Definition: kvj1939lib_types.h:121
uint32_t request_handle
A unique handle to the write request. Tx confirmation callback will be called with this handle if it ...
Definition: kvj1939lib_types.h:124