DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
nmea2000.cpp
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 nmea2000.cpp
6 * @brief NMEA 2000 codec (Fast Packet over J1939; pure, host-tested).
7 */
8
10
11#if DETWS_ENABLE_NMEA2000
12
13#include <string.h>
14
15uint8_t n2k_fastpacket_num_frames(uint16_t total_len)
16{
17 if (total_len <= N2K_FP_F0_DATA)
18 return 1;
19 return (uint8_t)(1u + (total_len - N2K_FP_F0_DATA + (N2K_FP_FN_DATA - 1)) / N2K_FP_FN_DATA);
20}
21
22bool n2k_fastpacket_build_frame(CanFrame *out, uint8_t seq, uint8_t frame_idx, uint8_t priority, uint32_t pgn,
23 uint8_t sa, uint8_t da, const uint8_t *data, uint16_t total_len)
24{
25 if (!out || !data || seq > 7 || total_len == 0 || total_len > DETWS_N2K_FP_MAX)
26 return false;
27 if (frame_idx >= n2k_fastpacket_num_frames(total_len))
28 return false;
29 uint32_t id;
30 if (!j1939_encode_id(&id, priority, pgn, sa, da))
31 return false;
32 out->id = id;
33 out->extended = true;
34 out->rtr = false;
35 out->dlc = DET_CAN_MAX_DLC; // Fast Packet frames are full 8-octet frames
36 memset(out->data, 0xFF, sizeof(out->data)); // pad unused octets with 0xFF
37
38 out->data[0] = (uint8_t)((seq << N2K_FP_SEQ_SHIFT) | (frame_idx & N2K_FP_FRAME_MASK));
39 if (frame_idx == 0)
40 {
41 out->data[1] = (uint8_t)total_len;
42 uint8_t n = total_len < N2K_FP_F0_DATA ? (uint8_t)total_len : (uint8_t)N2K_FP_F0_DATA;
43 memcpy(out->data + 2, data, n);
44 }
45 else
46 {
47 uint16_t off = (uint16_t)(N2K_FP_F0_DATA + (frame_idx - 1) * N2K_FP_FN_DATA);
48 uint16_t remaining = (uint16_t)(total_len - off);
49 uint8_t n = remaining < N2K_FP_FN_DATA ? (uint8_t)remaining : (uint8_t)N2K_FP_FN_DATA;
50 memcpy(out->data + 1, data + off, n);
51 }
52 return true;
53}
54
55void n2k_fastpacket_reset(N2kFastPacketRx *rx)
56{
57 if (rx)
58 memset(rx, 0, sizeof(*rx));
59}
60
61N2kFpResult n2k_fastpacket_feed(N2kFastPacketRx *rx, const CanFrame *f)
62{
63 if (!rx || !f || !f->extended || f->dlc < 2)
64 return N2kFpResult::N2K_FP_IGNORED;
65 J1939Id id;
66 if (!j1939_decode_id(f->id, &id))
67 return N2kFpResult::N2K_FP_IGNORED; // GCOVR_EXCL_LINE unreachable: j1939_decode_id only fails on a null out,
68 // and &id is non-null
69
70 uint8_t seq = (uint8_t)(f->data[0] >> N2K_FP_SEQ_SHIFT);
71 uint8_t frame_idx = (uint8_t)(f->data[0] & N2K_FP_FRAME_MASK);
72
73 if (frame_idx == 0) // first frame: total length + first 6 data octets
74 {
75 uint16_t total = f->data[1];
76 if (total == 0 || total > DETWS_N2K_FP_MAX)
77 return N2kFpResult::N2K_FP_ERR;
78 n2k_fastpacket_reset(rx);
79 rx->active = true;
80 rx->seq = seq;
81 rx->sa = id.sa;
82 rx->pgn = id.pgn;
83 rx->total_len = total;
84 uint8_t n = total < N2K_FP_F0_DATA ? (uint8_t)total : (uint8_t)N2K_FP_F0_DATA;
85 memcpy(rx->buf, f->data + 2, n);
86 rx->received = n;
87 rx->next_frame = 1;
88 if (rx->received >= total)
89 {
90 rx->active = false;
91 return N2kFpResult::N2K_FP_COMPLETE;
92 }
93 return N2kFpResult::N2K_FP_STARTED;
94 }
95
96 // continuation frame: must match the active sequence / source / PGN and be in order.
97 if (!rx->active || seq != rx->seq || id.sa != rx->sa || id.pgn != rx->pgn)
98 return N2kFpResult::N2K_FP_IGNORED;
99 if (frame_idx != rx->next_frame)
100 {
101 n2k_fastpacket_reset(rx);
102 return N2kFpResult::N2K_FP_ERR;
103 }
104 uint16_t remaining = (uint16_t)(rx->total_len - rx->received);
105 uint8_t n = remaining < N2K_FP_FN_DATA ? (uint8_t)remaining : (uint8_t)N2K_FP_FN_DATA;
106 memcpy(rx->buf + rx->received, f->data + 1, n);
107 rx->received = (uint16_t)(rx->received + n);
108 rx->next_frame++;
109 if (rx->received >= rx->total_len)
110 {
111 rx->active = false;
112 return N2kFpResult::N2K_FP_COMPLETE;
113 }
114 return N2kFpResult::N2K_FP_PROGRESS;
115}
116
117bool n2k_build_single(CanFrame *out, uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da, const uint8_t *data,
118 uint8_t len)
119{
120 return j1939_build_message(out, priority, pgn, sa, da, data, len);
121}
122
123#endif // DETWS_ENABLE_NMEA2000
#define DET_CAN_MAX_DLC
classic CAN carries at most 8 data octets.
Definition can.h:30
NMEA 2000 codec (DETWS_ENABLE_NMEA2000) - the marine instrumentation network, built on J1939 over CAN...
Definition can.h:44
uint8_t data[DET_CAN_MAX_DLC]
Definition can.h:49
bool rtr
Definition can.h:47
uint32_t id
Definition can.h:45
uint8_t dlc
Definition can.h:48
bool extended
Definition can.h:46