ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
nmea2000.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file nmea2000.h
6 * @brief NMEA 2000 codec (PROTOCORE_ENABLE_NMEA2000) - the marine instrumentation network, built on
7 * J1939 over CAN.
8 *
9 * NMEA 2000 is J1939 at the transport layer (the same 29-bit priority / PGN / source /
10 * destination identifier), so this codec reuses the J1939 id encode / decode
11 * (`PROTOCORE_ENABLE_NMEA2000` force-enables `PROTOCORE_ENABLE_J1939`). What it adds is the
12 * NMEA-specific **Fast Packet** transport: messages of 9..223 octets are split across CAN
13 * frames using a per-frame control octet (sequence counter + frame counter) instead of the
14 * J1939 BAM/CMDT protocol. The first frame carries the total length; continuations carry 7
15 * data octets each.
16 *
17 * Pure and host-tested. Drive it from the ESP32 TWAI peripheral or an MCP2515 over SPI to
18 * bridge an NMEA 2000 backbone (GPS, wind, depth, engine PGNs) onto Wi-Fi.
19 *
20 * @author Douglas Quigg (dstroy0)
21 * @date 2026
22 */
23
24#ifndef PROTOCORE_NMEA2000_H
25#define PROTOCORE_NMEA2000_H
26
27#include "protocore_config.h" // the entry point: protocore_types.h for the widths
28
29#if PROTOCORE_ENABLE_NMEA2000
30
32
33#include "services/fieldbus/j1939/j1939.h" // reuses the J1939 29-bit identifier codec
34#include "shared/can/can.h"
35
36#define N2K_FP_SEQ_SHIFT 5 ///< control octet: sequence counter in bits 7..5
37#define N2K_FP_FRAME_MASK 0x1Fu ///< control octet: frame counter in bits 4..0
38#define N2K_FP_F0_DATA 6u ///< data octets in the first frame (after control + length octets)
39#define N2K_FP_FN_DATA 7u ///< data octets in a continuation frame
40
41/** @brief Result of feeding a frame to the Fast Packet reassembler. */
42typedef enum PROTO_ENUM_PACKED
43{
44 N2K_FP_IGNORED = 0, ///< not part of the active sequence
45 N2K_FP_STARTED, ///< first frame opened a sequence
46 N2K_FP_PROGRESS, ///< a continuation frame was accepted
47 N2K_FP_COMPLETE, ///< the message is fully reassembled
48 N2K_FP_ERR, ///< out-of-order / too large
49} N2kFpResult;
50
51/** @brief Fast Packet reassembly context (one in-flight message). */
52typedef struct
53{
54 proto_bool active;
55 uint8_t seq; ///< sequence counter of the in-progress message
56 uint8_t sa; ///< source address
57 uint32_t pgn; ///< the message PGN
58 uint16_t total_len; ///< announced total length
59 uint16_t received; ///< octets stored so far
60 uint8_t next_frame; ///< next expected frame counter
61 uint8_t buf[PROTOCORE_N2K_FP_MAX];
62} N2kFastPacketRx;
63
64/** @brief Number of Fast Packet frames needed for @p total_len octets. */
65uint8_t protocore_n2k_fastpacket_num_frames(uint16_t total_len);
66
67/**
68 * @brief Build Fast Packet frame @p frame_idx (0-based) of a message.
69 * @p seq is the 0..7 sequence counter for this message; @p total_len is the whole payload.
70 */
71proto_bool protocore_n2k_fastpacket_build_frame(CanFrame *out, uint8_t seq, uint8_t frame_idx, uint8_t priority,
72 uint32_t pgn, uint8_t sa, uint8_t da, const uint8_t *data,
73 uint16_t total_len);
74
75/** @brief Reset a Fast Packet reassembly context to idle. */
76void protocore_n2k_fastpacket_reset(N2kFastPacketRx *rx);
77
78/** @brief Feed a received frame to the Fast Packet reassembler; see @ref N2kFpResult. */
79N2kFpResult protocore_n2k_fastpacket_feed(N2kFastPacketRx *rx, const CanFrame *f);
80
81/** @brief Build a single-frame (<= 8 octet) NMEA 2000 message (a thin wrap of J1939). */
82proto_bool protocore_n2k_build_single(CanFrame *out, uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da,
83 const uint8_t *data, uint8_t len);
84
85// --- typed decoders for common single-frame PGNs ---
86//
87// These decode a raw PGN payload (a single frame's data[] or a reassembled Fast Packet buffer) into
88// engineering units. The caller matches the PGN off the CAN id first, then calls the matching decoder.
89// NMEA 2000 marks a field "not available" with an all-ones raw (0xFFFF for a U2, 0x7FFFFFFF for the
90// signed lat/lon), which clears the field's validity flag.
91
92#define N2K_PGN_POSITION_RAPID 129025u ///< Position, Rapid Update: latitude + longitude
93#define N2K_PGN_COG_SOG_RAPID 129026u ///< COG & SOG, Rapid Update: course + speed over ground
94#define N2K_PGN_ENGINE_RAPID 127488u ///< Engine Parameters, Rapid Update: speed + boost + tilt/trim
95#define N2K_PGN_ENGINE_DYNAMIC 127489u ///< Engine Parameters, Dynamic: oil / coolant / hours / load / ...
96#define N2K_PGN_WIND_DATA 130306u ///< Wind Data: speed + angle + reference
97#define N2K_PGN_SPEED 128259u ///< Speed: water-referenced + ground-referenced speed
98#define N2K_PGN_WATER_DEPTH 128267u ///< Water Depth: depth below transducer + offset
99#define N2K_PGN_VESSEL_HEADING 127250u ///< Vessel Heading: heading + deviation + variation
100#define N2K_PGN_RUDDER 127245u ///< Rudder: direction order + angle order + position
101#define N2K_PGN_ATTITUDE 127257u ///< Attitude: yaw + pitch + roll
102#define N2K_PGN_TEMPERATURE 130312u ///< Temperature: instance + source + actual / set temperature
103
104// Temperature source (PGN 130312 byte 2).
105#define N2K_TEMP_SRC_SEA 0
106#define N2K_TEMP_SRC_OUTSIDE 1
107#define N2K_TEMP_SRC_INSIDE 2
108#define N2K_TEMP_SRC_ENGINE_ROOM 3
109#define N2K_TEMP_SRC_MAIN_CABIN 4
110#define N2K_TEMP_SRC_LIVE_WELL 5
111#define N2K_TEMP_SRC_BAIT_WELL 6
112#define N2K_TEMP_SRC_REFRIGERATION 7
113#define N2K_TEMP_SRC_HEATING_SYSTEM 8
114#define N2K_TEMP_SRC_FREEZER 13
115#define N2K_TEMP_SRC_EXHAUST_GAS 14
116
117// COG reference (PGN 129026 byte 1, low 2 bits).
118#define N2K_COG_REF_TRUE 0 ///< course over ground referenced to True North
119#define N2K_COG_REF_MAGNETIC 1 ///< course over ground referenced to Magnetic North
120#define N2K_COG_REF_ERROR 2 ///< reference in error
121#define N2K_COG_REF_NULL 3 ///< reference not available / null
122
123// Heading reference (PGN 127250 byte 7, low 2 bits).
124#define N2K_HEADING_REF_TRUE 0 ///< true heading
125#define N2K_HEADING_REF_MAGNETIC 1 ///< magnetic heading
126
127// Wind reference (PGN 130306 byte 5, low 3 bits).
128#define N2K_WIND_REF_TRUE_NORTH 0 ///< true, referenced to North
129#define N2K_WIND_REF_MAGNETIC 1 ///< magnetic, referenced to North
130#define N2K_WIND_REF_APPARENT 2 ///< apparent
131#define N2K_WIND_REF_TRUE_BOAT 3 ///< true, referenced to the vessel (boat)
132#define N2K_WIND_REF_TRUE_WATER 4 ///< true, referenced to the water
133
134/** @brief Decoded Position Rapid Update (PGN 129025). */
135typedef struct
136{
137 proto_bool valid; ///< false if either coordinate is not-available
138 double lat_deg; ///< latitude in decimal degrees (1e-7 deg/bit)
139 double lon_deg; ///< longitude in decimal degrees
140} N2kPositionRapid;
141
142/** @brief Decoded COG & SOG Rapid Update (PGN 129026). */
143typedef struct
144{
145 uint8_t sid; ///< sequence id
146 uint8_t cog_ref; ///< course reference (@ref N2K_COG_REF_TRUE / _MAGNETIC)
147 proto_bool cog_valid; ///< false if the course over ground is not-available
148 float cog_rad; ///< course over ground (radians, 0.0001 rad per bit)
149 proto_bool sog_valid; ///< false if the speed over ground is not-available
150 float sog_mps; ///< speed over ground (m/s, 0.01 m/s per bit)
151} N2kCogSogRapid;
152
153/** @brief Decoded Engine Parameters, Rapid Update (PGN 127488). */
154typedef struct
155{
156 uint8_t instance; ///< engine instance (0 = single / port, 1 = starboard, ...)
157 proto_bool speed_valid; ///< false if the engine speed is not-available
158 float speed_rpm; ///< engine speed (rpm, 0.25 rpm per bit)
159 proto_bool boost_valid; ///< false if the boost pressure is not-available
160 float boost_pa; ///< engine boost pressure (Pa, 100 Pa per bit)
161 proto_bool tilt_valid; ///< false if the tilt/trim is not-available
162 int8_t tilt_pct; ///< engine tilt / trim (percent, 1 %/bit, signed)
163} N2kEngineRapid;
164
165// Rudder direction order (PGN 127245 byte 1, low 3 bits).
166#define N2K_RUDDER_NO_ORDER 0
167#define N2K_RUDDER_MOVE_TO_STARBOARD 1
168#define N2K_RUDDER_MOVE_TO_PORT 2
169
170/** @brief Decoded Rudder (PGN 127245): the commanded and actual rudder state. */
171typedef struct
172{
173 uint8_t instance; ///< rudder instance
174 uint8_t direction_order; ///< commanded direction (@ref N2K_RUDDER_NO_ORDER etc.)
175 proto_bool angle_order_valid; ///< false if the angle order is not-available
176 float angle_order_rad; ///< commanded rudder angle (radians, 0.0001 rad per bit)
177 proto_bool position_valid; ///< false if the position is not-available
178 float position_rad; ///< actual rudder position (radians, 0.0001 rad per bit)
179} N2kRudder;
180
181/** @brief Decoded Attitude (PGN 127257): the vessel's orientation. Each angle is signed at 0.0001 rad/bit. */
182typedef struct
183{
184 uint8_t sid; ///< sequence id
185 proto_bool yaw_valid; ///< false if yaw is not-available
186 float yaw_rad; ///< yaw (radians); + = bow rotating to starboard
187 proto_bool pitch_valid; ///< false if pitch is not-available
188 float pitch_rad; ///< pitch (radians); + = bow up
189 proto_bool roll_valid; ///< false if roll is not-available
190 float roll_rad; ///< roll (radians); + = starboard side down
191} N2kAttitude;
192
193/** @brief Decoded Temperature (PGN 130312). Temperatures are carried in Kelvin (0.01 K/bit) on the wire and
194 * exposed here in Celsius. */
195typedef struct
196{
197 uint8_t sid; ///< sequence id
198 uint8_t instance; ///< temperature instance
199 uint8_t source; ///< temperature source (@ref N2K_TEMP_SRC_SEA etc.)
200 proto_bool actual_valid; ///< false if the actual temperature is not-available
201 float actual_c; ///< actual temperature (degrees Celsius)
202 proto_bool set_valid; ///< false if the set/target temperature is not-available
203 float set_c; ///< set / target temperature (degrees Celsius)
204} N2kTemperature;
205
206/** @brief Decoded Engine Parameters, Dynamic (PGN 127489): the full engine-monitoring picture. Each measured
207 * field clears its validity flag for a not-available raw; the two discrete-status words are raw bitfields. */
208typedef struct // NOSONAR(cpp:S1820): one decoded PGN is one logical message; the field count mirrors the
209 // protocol's signals, so splitting the struct would be artificial, not clearer
210{
211 uint8_t instance; ///< engine instance
212 proto_bool oil_pressure_valid;
213 float oil_pressure_pa; ///< engine oil pressure (Pa, 100 Pa per bit)
214 proto_bool oil_temp_valid;
215 float oil_temp_c; ///< engine oil temperature (degrees Celsius; 0.1 K/bit on the wire)
216 proto_bool coolant_temp_valid;
217 float coolant_temp_c; ///< engine coolant temperature (degrees Celsius; 0.01 K/bit on the wire)
218 proto_bool alt_voltage_valid;
219 float alt_voltage_v; ///< alternator potential (V, 0.01 V per bit, signed)
220 proto_bool fuel_rate_valid;
221 float fuel_rate_lph; ///< fuel rate (L/h, 0.1 L/h per bit, signed)
222 proto_bool engine_hours_valid;
223 uint32_t engine_hours_s; ///< total engine hours (seconds, 1 s per bit)
224 proto_bool coolant_pressure_valid;
225 float coolant_pressure_pa; ///< coolant pressure (Pa, 100 Pa per bit)
226 proto_bool fuel_pressure_valid;
227 float fuel_pressure_pa; ///< fuel pressure (Pa, 1000 Pa per bit)
228 uint16_t discrete_status_1; ///< discrete status 1 (raw bitfield)
229 uint16_t discrete_status_2; ///< discrete status 2 (raw bitfield)
230 proto_bool load_valid;
231 int8_t load_pct; ///< percent engine load (%, 1 %/bit, signed)
232 proto_bool torque_valid;
233 int8_t torque_pct; ///< percent engine torque (%, 1 %/bit, signed)
234} N2kEngineDynamic;
235
236/** @brief Decoded Wind Data (PGN 130306). */
237typedef struct
238{
239 uint8_t sid; ///< sequence id
240 proto_bool speed_valid; ///< false if the wind speed is not-available
241 float speed_mps; ///< wind speed (m/s, 0.01 m/s per bit)
242 proto_bool angle_valid; ///< false if the wind angle is not-available
243 float angle_rad; ///< wind angle (radians, 0.0001 rad per bit)
244 uint8_t reference; ///< wind reference (@ref N2K_WIND_REF_TRUE_NORTH etc.)
245} N2kWindData;
246
247// Speed water-referenced sensor type (PGN 128259 byte 5).
248#define N2K_SPEED_TYPE_PADDLE_WHEEL 0
249#define N2K_SPEED_TYPE_PITOT_TUBE 1
250#define N2K_SPEED_TYPE_DOPPLER 2
251#define N2K_SPEED_TYPE_CORRELATION 3 ///< correlation / ultrasound
252#define N2K_SPEED_TYPE_ELECTROMAGNETIC 4 ///< electromagnetic
253
254/** @brief Decoded Speed (PGN 128259): through-water and over-ground speed. */
255typedef struct
256{
257 uint8_t sid; ///< sequence id
258 proto_bool water_valid; ///< false if the water-referenced speed is not-available
259 float water_mps; ///< speed through water (m/s, 0.01 m/s per bit)
260 proto_bool ground_valid; ///< false if the ground-referenced speed is not-available
261 float ground_mps; ///< speed over ground (m/s, 0.01 m/s per bit)
262 uint8_t water_ref_type; ///< water-speed sensor type (@ref N2K_SPEED_TYPE_PADDLE_WHEEL etc.)
263} N2kSpeed;
264
265/** @brief Decoded Water Depth (PGN 128267). */
266typedef struct
267{
268 uint8_t sid; ///< sequence id
269 proto_bool depth_valid; ///< false if the depth is not-available
270 float depth_m; ///< water depth below the transducer (metres, 0.01 m per bit)
271 float offset_m; ///< transducer offset (m); positive = distance to waterline, negative = to keel
272} N2kWaterDepth;
273
274/** @brief Decoded Vessel Heading (PGN 127250). */
275typedef struct
276{
277 uint8_t sid; ///< sequence id
278 proto_bool heading_valid; ///< false if the heading is not-available
279 float heading_rad; ///< heading (radians, 0.0001 rad per bit)
280 float deviation_rad; ///< magnetic deviation (radians)
281 float variation_rad; ///< magnetic variation (radians)
282 uint8_t reference; ///< heading reference (@ref N2K_HEADING_REF_TRUE / _MAGNETIC)
283} N2kVesselHeading;
284
285/**
286 * @brief Decode a Position Rapid Update (PGN 129025) payload into @p out.
287 * @return true iff @p len is at least 8 octets; false otherwise.
288 */
289proto_bool protocore_n2k_decode_position_rapid(const uint8_t *payload, size_t len, N2kPositionRapid *out);
290
291/**
292 * @brief Decode a COG & SOG Rapid Update (PGN 129026) payload into @p out.
293 * @return true iff @p len is at least 6 octets (SID + reference + COG + SOG); false otherwise.
294 */
295proto_bool protocore_n2k_decode_cog_sog_rapid(const uint8_t *payload, size_t len, N2kCogSogRapid *out);
296
297/**
298 * @brief Decode an Engine Parameters Rapid Update (PGN 127488) payload into @p out.
299 * @return true iff @p len is at least 6 octets (instance + speed + boost + tilt); false otherwise.
300 */
301proto_bool protocore_n2k_decode_engine_rapid(const uint8_t *payload, size_t len, N2kEngineRapid *out);
302
303/**
304 * @brief Decode an Engine Parameters Dynamic (PGN 127489) payload into @p out. This is a Fast Packet PGN, so
305 * @p payload is the reassembled message body.
306 * @return true iff @p len is at least 26 octets (the full engine-parameters record); false otherwise.
307 */
308proto_bool protocore_n2k_decode_engine_dynamic(const uint8_t *payload, size_t len, N2kEngineDynamic *out);
309
310/**
311 * @brief Decode a Rudder (PGN 127245) payload into @p out.
312 * @return true iff @p len is at least 6 octets (instance + direction + angle order + position); false otherwise.
313 */
314proto_bool protocore_n2k_decode_rudder(const uint8_t *payload, size_t len, N2kRudder *out);
315
316/**
317 * @brief Decode an Attitude (PGN 127257) payload into @p out.
318 * @return true iff @p len is at least 7 octets (SID + yaw + pitch + roll); false otherwise.
319 */
320proto_bool protocore_n2k_decode_attitude(const uint8_t *payload, size_t len, N2kAttitude *out);
321
322/**
323 * @brief Decode a Temperature (PGN 130312) payload into @p out.
324 * @return true iff @p len is at least 7 octets (SID + instance + source + actual + set); false otherwise.
325 */
326proto_bool protocore_n2k_decode_temperature(const uint8_t *payload, size_t len, N2kTemperature *out);
327
328/** @brief Decoded Battery Status (PGN 127508): a battery bank's voltage, current, and temperature. Each
329 * measurement clears a validity flag for a not-available raw. */
330typedef struct
331{
332 uint8_t instance; ///< battery instance
333 proto_bool voltage_valid; ///< false when the raw voltage is in the not-available range
334 float voltage_v; ///< battery voltage (V, 0.01 V/bit)
335 proto_bool current_valid;
336 float current_a; ///< battery current (A, 0.1 A/bit, signed)
337 proto_bool temp_valid;
338 float temp_c; ///< battery temperature (degrees C; carried as Kelvin at 0.01 K/bit on the wire)
339 uint8_t sid; ///< sequence id (correlates related messages)
340} N2kBatteryStatus;
341
342/**
343 * @brief Decode a Battery Status (PGN 127508) payload into @p out: instance + voltage (2, signed, 0.01 V) +
344 * current (2, signed, 0.1 A) + temperature (2, unsigned, 0.01 K) + SID.
345 * @return true iff @p len is at least 8 octets; false otherwise.
346 */
347proto_bool protocore_n2k_decode_battery_status(const uint8_t *payload, size_t len, N2kBatteryStatus *out);
348
349// Fluid / tank type (PGN 127505 byte 0, high nibble).
350#define PROTOCORE_N2K_FLUID_FUEL 0
351#define PROTOCORE_N2K_FLUID_WATER 1
352#define PROTOCORE_N2K_FLUID_GRAY_WATER 2
353#define PROTOCORE_N2K_FLUID_LIVE_WELL 3
354#define PROTOCORE_N2K_FLUID_OIL 4
355#define PROTOCORE_N2K_FLUID_BLACK_WATER 5
356
357/** @brief Decoded Fluid Level (PGN 127505): a tank's fill level and total capacity. */
358typedef struct
359{
360 uint8_t instance; ///< tank instance (0..15)
361 uint8_t fluid_type; ///< fluid / tank type (PROTOCORE_N2K_FLUID_*)
362 proto_bool level_valid; ///< false when the raw level is in the not-available range
363 float level_pct; ///< tank level as a percentage full (0.004 %/bit)
364 proto_bool capacity_valid;
365 float capacity_l; ///< total tank capacity (litres, 0.1 L/bit)
366} N2kFluidLevel;
367
368/**
369 * @brief Decode a Fluid Level (PGN 127505) payload into @p out: the instance + fluid type (packed in byte 0),
370 * the level (2, signed, 0.004 %) and the total capacity (4, unsigned, 0.1 L).
371 * @return true iff @p len is at least 7 octets; false otherwise.
372 */
373proto_bool protocore_n2k_decode_fluid_level(const uint8_t *payload, size_t len, N2kFluidLevel *out);
374
375// Pressure source (PGN 130314 byte 2).
376#define PROTOCORE_N2K_PRESSURE_ATMOSPHERIC 0
377#define PROTOCORE_N2K_PRESSURE_WATER 1
378#define PROTOCORE_N2K_PRESSURE_STEAM 2
379#define PROTOCORE_N2K_PRESSURE_COMPRESSED_AIR 3
380#define PROTOCORE_N2K_PRESSURE_HYDRAULIC 4
381#define PROTOCORE_N2K_PRESSURE_FILTER 5
382#define PROTOCORE_N2K_PRESSURE_ALTIMETER_SETTING 6
383#define PROTOCORE_N2K_PRESSURE_OIL 7
384#define PROTOCORE_N2K_PRESSURE_FUEL 8
385
386/** @brief Decoded Actual Pressure (PGN 130314): a measured pressure from one of several sources. */
387typedef struct
388{
389 uint8_t sid; ///< sequence id (correlates related messages)
390 uint8_t instance; ///< pressure instance
391 uint8_t source; ///< pressure source (PROTOCORE_N2K_PRESSURE_*)
392 proto_bool pressure_valid; ///< false when the raw pressure is in the not-available range
393 float pressure_pa; ///< measured pressure (Pa, 0.1 Pa/bit, signed)
394} N2kActualPressure;
395
396/**
397 * @brief Decode an Actual Pressure (PGN 130314) payload into @p out: SID + instance + source + pressure
398 * (4, signed, 0.1 Pa/bit). The trailing reserved octet is not required.
399 * @return true iff @p len is at least 7 octets (SID + instance + source + pressure); false otherwise.
400 */
401proto_bool protocore_n2k_decode_actual_pressure(const uint8_t *payload, size_t len, N2kActualPressure *out);
402
403/**
404 * @brief Decode a Wind Data (PGN 130306) payload into @p out.
405 * @return true iff @p len is at least 6 octets; false otherwise.
406 */
407proto_bool protocore_n2k_decode_wind_data(const uint8_t *payload, size_t len, N2kWindData *out);
408
409/**
410 * @brief Decode a Speed (PGN 128259) payload into @p out.
411 * @return true iff @p len is at least 6 octets (SID + water speed + ground speed + type); false otherwise.
412 */
413proto_bool protocore_n2k_decode_speed(const uint8_t *payload, size_t len, N2kSpeed *out);
414
415/**
416 * @brief Decode a Water Depth (PGN 128267) payload into @p out.
417 * @return true iff @p len is at least 7 octets (SID + depth + offset); false otherwise.
418 */
419proto_bool protocore_n2k_decode_water_depth(const uint8_t *payload, size_t len, N2kWaterDepth *out);
420
421/**
422 * @brief Decode a Vessel Heading (PGN 127250) payload into @p out.
423 * @return true iff @p len is at least 8 octets; false otherwise.
424 */
425proto_bool protocore_n2k_decode_vessel_heading(const uint8_t *payload, size_t len, N2kVesselHeading *out);
426
428
429#endif // PROTOCORE_ENABLE_NMEA2000
430
431#endif // PROTOCORE_NMEA2000_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
Shared CAN 2.0 frame type for the CAN-based industrial codecs (one source of truth).
SAE J1939 message codec (PROTOCORE_ENABLE_J1939) - the heavy-duty-vehicle / agriculture / marine / ge...
Definition can.h:44
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97