DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
j1939.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file j1939.h
6 * @brief SAE J1939 message codec (DETWS_ENABLE_J1939) - the heavy-duty-vehicle / agriculture /
7 * marine / genset CAN higher-layer protocol, over 29-bit extended CAN frames.
8 *
9 * J1939 packs a 29-bit extended identifier as:
10 * @code
11 * bits 28-26 Priority | 25 EDP | 24 DP | 23-16 PF | 15-8 PS | 7-0 SA
12 * @endcode
13 * The 18-bit Parameter Group Number (PGN) is EDP|DP|PF|PS, where PS is part of the PGN only
14 * for PDU2 (broadcast, PF >= 240); for PDU1 (PF < 240) PS is the destination address (DA) and
15 * the PGN's low octet is 0. This codec encodes / decodes that id, builds single-frame
16 * messages, runs the Transport Protocol (BAM broadcast + RTS/CTS connection mode) with a
17 * reassembler for messages up to `DETWS_J1939_TP_MAX` octets, and builds the Address Claimed
18 * (with a 64-bit NAME) and Request PGN messages.
19 *
20 * Pure and host-tested. Drive it from the ESP32 TWAI peripheral (or an MCP2515 over SPI) to
21 * bridge a J1939 bus onto Wi-Fi - decode engine / transmission / genset PGNs and publish them.
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef DETERMINISTICESPASYNCWEBSERVER_J1939_H
28#define DETERMINISTICESPASYNCWEBSERVER_J1939_H
29
30#include "ServerConfig.h"
31
32#if DETWS_ENABLE_J1939
33
35#include <stddef.h>
36#include <stdint.h>
37
38// Well-known PGNs and addresses.
39#define J1939_PGN_TP_CM 0x00EC00u ///< Transport Protocol - Connection Management (60416)
40#define J1939_PGN_TP_DT 0x00EB00u ///< Transport Protocol - Data Transfer (60160)
41#define J1939_PGN_ADDRESS_CLAIM 0x00EE00u ///< Address Claimed / Cannot Claim (60928)
42#define J1939_PGN_REQUEST 0x00EA00u ///< Request PGN (59904)
43#define J1939_ADDR_GLOBAL 0xFFu ///< broadcast destination address
44#define J1939_ADDR_NULL 0xFEu ///< null / unclaimed source address
45#define J1939_PDU2_THRESHOLD 240u ///< PF >= 240 is PDU2 (broadcast); < 240 is PDU1 (peer)
46
47// TP.CM control bytes (data[0] of a TP.CM frame).
48#define J1939_TP_CM_RTS 0x10u ///< Request To Send (connection mode)
49#define J1939_TP_CM_CTS 0x11u ///< Clear To Send
50#define J1939_TP_CM_EOM_ACK 0x13u ///< End Of Message Acknowledge
51#define J1939_TP_CM_BAM 0x20u ///< Broadcast Announce Message
52#define J1939_TP_CM_ABORT 0xFFu ///< Connection Abort
53
54#define J1939_TP_DT_LEN 7u ///< data octets carried per TP.DT packet (1 seq byte + 7 data)
55
56/** @brief A decoded J1939 identifier. */
57struct J1939Id
58{
59 uint8_t priority; ///< 0 (highest) .. 7
60 uint32_t pgn; ///< 18-bit Parameter Group Number
61 uint8_t sa; ///< source address
62 uint8_t da; ///< destination address (PDU1), or J1939_ADDR_GLOBAL (PDU2)
63 uint8_t pf; ///< PDU format
64 uint8_t ps; ///< PDU specific (DA for PDU1, group extension for PDU2)
65 bool pdu1; ///< true => peer-to-peer (PF < 240); false => broadcast
66};
67
68/** @brief Result of feeding a frame to the TP reassembler. */
69enum class J1939TpResult : uint8_t
70{
71 J1939_TP_IGNORED = 0, ///< not a TP frame for the active session
72 J1939_TP_STARTED, ///< a BAM / RTS opened a session
73 J1939_TP_PROGRESS, ///< a data packet was accepted, more to come
74 J1939_TP_COMPLETE, ///< the message is fully reassembled (see fields below)
75 J1939_TP_ERROR, ///< malformed / out-of-sequence / too large
76};
77
78/** @brief Transport-Protocol reassembly context (one in-flight message). */
79struct J1939TpRx
80{
81 bool active;
82 uint8_t sa; ///< source of the session
83 uint32_t pgn; ///< the transported PGN
84 uint16_t total_size; ///< announced message size
85 uint8_t num_packets; ///< announced packet count
86 uint8_t next_seq; ///< next expected sequence number (1-based)
87 uint16_t received; ///< octets stored so far
88 uint8_t buf[DETWS_J1939_TP_MAX];
89};
90
91// --- identifier ---
92
93/** @brief Encode a 29-bit J1939 id. @p da is used only for a PDU1 (PF < 240) PGN. */
94bool j1939_encode_id(uint32_t *id, uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da);
95
96/** @brief Decode a 29-bit J1939 id into its fields. */
97bool j1939_decode_id(uint32_t id, J1939Id *out);
98
99// --- single-frame messages ---
100
101/** @brief Build a single-frame (<= 8 octet) J1939 message. */
102bool j1939_build_message(CanFrame *out, uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da, const uint8_t *data,
103 uint8_t len);
104
105/** @brief Build a Request-PGN frame asking @p da for @p requested_pgn. */
106bool j1939_build_request(CanFrame *out, uint8_t sa, uint8_t da, uint32_t requested_pgn);
107
108/** @brief Build an Address-Claimed frame announcing @p sa with the 64-bit @p name. */
109bool j1939_build_address_claim(CanFrame *out, uint8_t sa, uint64_t name);
110
111/** @brief Compose a 64-bit J1939 NAME from its fields (see J1939-81). */
112uint64_t j1939_build_name(bool arbitrary_address_capable, uint8_t industry_group, uint8_t vehicle_system_instance,
113 uint8_t vehicle_system, uint8_t function, uint8_t function_instance, uint8_t ecu_instance,
114 uint16_t manufacturer_code, uint32_t identity_number);
115
116// --- transport protocol (multi-packet) ---
117
118/** @brief Octet count -> TP packet count (ceil(size / 7)). */
119uint8_t j1939_tp_num_packets(uint16_t total_size);
120
121/** @brief Build the BAM (broadcast) TP.CM announce frame for @p pgn / @p total_size. */
122bool j1939_build_bam_cm(CanFrame *out, uint8_t sa, uint32_t pgn, uint16_t total_size);
123
124/** @brief Build TP.DT data packet @p seq (1-based) carrying @p chunk_len (1..7) octets. */
125bool j1939_build_tp_dt(CanFrame *out, uint8_t sa, uint8_t da, uint8_t seq, const uint8_t *chunk, uint8_t chunk_len);
126
127/** @brief Reset a reassembly context to idle. */
128void j1939_tp_reset(J1939TpRx *rx);
129
130/** @brief Feed a received frame to the reassembler; see @ref J1939TpResult. */
131J1939TpResult j1939_tp_feed(J1939TpRx *rx, const CanFrame *f);
132
133#endif // DETWS_ENABLE_J1939
134#endif // DETERMINISTICESPASYNCWEBSERVER_J1939_H
User-facing configuration for DeterministicESPAsyncWebServer.
Shared CAN 2.0 frame type for the CAN-based industrial codecs (one source of truth).
Definition can.h:44