15uint8_t pc_n2k_fastpacket_num_frames(uint16_t total_len)
17 if (total_len <= N2K_FP_F0_DATA)
21 return (uint8_t)(1u + (total_len - N2K_FP_F0_DATA + (N2K_FP_FN_DATA - 1)) / N2K_FP_FN_DATA);
24bool pc_n2k_fastpacket_build_frame(
CanFrame *out, uint8_t seq, uint8_t frame_idx, uint8_t priority, uint32_t pgn,
25 uint8_t sa, uint8_t da,
const uint8_t *data, uint16_t total_len)
27 if (!out || !data || seq > 7 || total_len == 0 || total_len > PC_N2K_FP_MAX)
31 if (frame_idx >= pc_n2k_fastpacket_num_frames(total_len))
36 if (!pc_j1939_encode_id(&
id, priority, pgn, sa, da))
44 memset(out->
data, 0xFF,
sizeof(out->
data));
46 out->
data[0] = (uint8_t)((seq << N2K_FP_SEQ_SHIFT) | (frame_idx & N2K_FP_FRAME_MASK));
49 out->
data[1] = (uint8_t)total_len;
50 uint8_t n = total_len < N2K_FP_F0_DATA ? (uint8_t)total_len : (uint8_t)N2K_FP_F0_DATA;
51 memcpy(out->
data + 2, data, n);
55 uint16_t off = (uint16_t)(N2K_FP_F0_DATA + (frame_idx - 1) * N2K_FP_FN_DATA);
56 uint16_t remaining = (uint16_t)(total_len - off);
57 uint8_t n = remaining < N2K_FP_FN_DATA ? (uint8_t)remaining : (uint8_t)N2K_FP_FN_DATA;
58 memcpy(out->
data + 1, data + off, n);
63void pc_n2k_fastpacket_reset(N2kFastPacketRx *rx)
67 memset(rx, 0,
sizeof(*rx));
71N2kFpResult pc_n2k_fastpacket_feed(N2kFastPacketRx *rx,
const CanFrame *f)
75 return N2kFpResult::N2K_FP_IGNORED;
78 if (!pc_j1939_decode_id(f->
id, &
id))
81 return N2kFpResult::N2K_FP_IGNORED;
85 uint8_t seq = (uint8_t)(f->
data[0] >> N2K_FP_SEQ_SHIFT);
86 uint8_t frame_idx = (uint8_t)(f->
data[0] & N2K_FP_FRAME_MASK);
90 uint16_t total = f->
data[1];
91 if (total == 0 || total > PC_N2K_FP_MAX)
93 return N2kFpResult::N2K_FP_ERR;
95 pc_n2k_fastpacket_reset(rx);
100 rx->total_len = total;
101 uint8_t n = total < N2K_FP_F0_DATA ? (uint8_t)total : (uint8_t)N2K_FP_F0_DATA;
102 memcpy(rx->buf, f->
data + 2, n);
105 if (rx->received >= total)
108 return N2kFpResult::N2K_FP_COMPLETE;
110 return N2kFpResult::N2K_FP_STARTED;
114 if (!rx->active || seq != rx->seq ||
id.sa != rx->sa ||
id.pgn != rx->pgn)
116 return N2kFpResult::N2K_FP_IGNORED;
118 if (frame_idx != rx->next_frame)
120 pc_n2k_fastpacket_reset(rx);
121 return N2kFpResult::N2K_FP_ERR;
123 uint16_t remaining = (uint16_t)(rx->total_len - rx->received);
124 uint8_t n = remaining < N2K_FP_FN_DATA ? (uint8_t)remaining : (uint8_t)N2K_FP_FN_DATA;
125 memcpy(rx->buf + rx->received, f->
data + 1, n);
126 rx->received = (uint16_t)(rx->received + n);
128 if (rx->received >= rx->total_len)
131 return N2kFpResult::N2K_FP_COMPLETE;
133 return N2kFpResult::N2K_FP_PROGRESS;
136bool pc_n2k_build_single(
CanFrame *out, uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da,
const uint8_t *data,
139 return pc_j1939_build_message(out, priority, pgn, sa, da, data, len);
146uint16_t rd_u16le(
const uint8_t *p)
148 return (uint16_t)(p[0] | ((uint16_t)p[1] << 8));
150int32_t rd_i32le(
const uint8_t *p)
152 return (int32_t)((uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24));
154uint32_t rd_u32le(
const uint8_t *p)
156 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
158int16_t rd_i16le(
const uint8_t *p)
160 return (int16_t)rd_u16le(p);
164bool pc_n2k_decode_position_rapid(
const uint8_t *payload,
size_t len, N2kPositionRapid *out)
166 if (!payload || !out || len < 8)
170 int32_t lat = rd_i32le(payload);
171 int32_t lon = rd_i32le(payload + 4);
172 out->valid = (lat != (int32_t)0x7FFFFFFF && lon != (int32_t)0x7FFFFFFF);
173 out->lat_deg = (double)lat * 1e-7;
174 out->lon_deg = (double)lon * 1e-7;
178bool pc_n2k_decode_cog_sog_rapid(
const uint8_t *payload,
size_t len, N2kCogSogRapid *out)
180 if (!payload || !out || len < 6)
184 out->sid = payload[0];
185 out->cog_ref = (uint8_t)(payload[1] & 0x03u);
186 uint16_t cog = rd_u16le(payload + 2);
187 uint16_t sog = rd_u16le(payload + 4);
188 out->cog_valid = (cog != 0xFFFFu);
189 out->cog_rad = (float)cog * 0.0001f;
190 out->sog_valid = (sog != 0xFFFFu);
191 out->sog_mps = (float)sog * 0.01f;
195bool pc_n2k_decode_engine_rapid(
const uint8_t *payload,
size_t len, N2kEngineRapid *out)
197 if (!payload || !out || len < 6)
201 out->instance = payload[0];
202 uint16_t rpm = rd_u16le(payload + 1);
203 out->speed_valid = (rpm != 0xFFFFu);
204 out->speed_rpm = (float)rpm * 0.25f;
205 uint16_t boost = rd_u16le(payload + 3);
206 out->boost_valid = (boost != 0xFFFFu);
207 out->boost_pa = (float)boost * 100.0f;
208 out->tilt_valid = (payload[5] != 0x7Fu);
209 out->tilt_pct = (int8_t)payload[5];
213bool pc_n2k_decode_temperature(
const uint8_t *payload,
size_t len, N2kTemperature *out)
215 if (!payload || !out || len < 7)
219 out->sid = payload[0];
220 out->instance = payload[1];
221 out->source = payload[2];
222 uint16_t act = rd_u16le(payload + 3);
223 out->actual_valid = (act != 0xFFFFu);
224 out->actual_c = (float)act * 0.01f - 273.15f;
225 uint16_t set = rd_u16le(payload + 5);
226 out->set_valid = (set != 0xFFFFu);
227 out->set_c = (float)set * 0.01f - 273.15f;
231bool pc_n2k_decode_battery_status(
const uint8_t *payload,
size_t len, N2kBatteryStatus *out)
233 if (!payload || !out || len < 8)
237 out->instance = payload[0];
238 int16_t v = rd_i16le(payload + 1);
239 out->voltage_valid = (v != (int16_t)0x7FFF);
240 out->voltage_v = (float)v * 0.01f;
241 int16_t c = rd_i16le(payload + 3);
242 out->current_valid = (c != (int16_t)0x7FFF);
243 out->current_a = (float)c * 0.1f;
244 uint16_t t = rd_u16le(payload + 5);
245 out->temp_valid = (t != 0xFFFFu);
246 out->temp_c = (float)t * 0.01f - 273.15f;
247 out->sid = payload[7];
251bool pc_n2k_decode_fluid_level(
const uint8_t *payload,
size_t len, N2kFluidLevel *out)
253 if (!payload || !out || len < 7)
257 out->instance = (uint8_t)(payload[0] & 0x0F);
258 out->fluid_type = (uint8_t)((payload[0] >> 4) & 0x0F);
259 int16_t lv = rd_i16le(payload + 1);
260 out->level_valid = (lv != (int16_t)0x7FFF);
261 out->level_pct = (float)lv * 0.004f;
262 uint32_t cap = rd_u32le(payload + 3);
263 out->capacity_valid = (cap != 0xFFFFFFFFu);
264 out->capacity_l = (float)cap * 0.1f;
268bool pc_n2k_decode_actual_pressure(
const uint8_t *payload,
size_t len, N2kActualPressure *out)
270 if (!payload || !out || len < 7)
274 out->sid = payload[0];
275 out->instance = payload[1];
276 out->source = payload[2];
277 int32_t p = rd_i32le(payload + 3);
278 out->pressure_valid = (p != (int32_t)0x7FFFFFFF);
279 out->pressure_pa = (float)p * 0.1f;
283bool pc_n2k_decode_rudder(
const uint8_t *payload,
size_t len, N2kRudder *out)
285 if (!payload || !out || len < 6)
289 out->instance = payload[0];
290 out->direction_order = (uint8_t)(payload[1] & 0x07u);
291 int16_t angle = rd_i16le(payload + 2);
292 out->angle_order_valid = ((uint16_t)angle != 0x7FFFu);
293 out->angle_order_rad = (float)angle * 0.0001f;
294 int16_t pos = rd_i16le(payload + 4);
295 out->position_valid = ((uint16_t)pos != 0x7FFFu);
296 out->position_rad = (float)pos * 0.0001f;
300bool pc_n2k_decode_attitude(
const uint8_t *payload,
size_t len, N2kAttitude *out)
302 if (!payload || !out || len < 7)
306 out->sid = payload[0];
307 int16_t yaw = rd_i16le(payload + 1);
308 out->yaw_valid = ((uint16_t)yaw != 0x7FFFu);
309 out->yaw_rad = (float)yaw * 0.0001f;
310 int16_t pitch = rd_i16le(payload + 3);
311 out->pitch_valid = ((uint16_t)pitch != 0x7FFFu);
312 out->pitch_rad = (float)pitch * 0.0001f;
313 int16_t roll = rd_i16le(payload + 5);
314 out->roll_valid = ((uint16_t)roll != 0x7FFFu);
315 out->roll_rad = (float)roll * 0.0001f;
319bool pc_n2k_decode_engine_dynamic(
const uint8_t *payload,
size_t len, N2kEngineDynamic *out)
321 if (!payload || !out || len < 26)
325 out->instance = payload[0];
326 uint16_t oilp = rd_u16le(payload + 1);
327 out->oil_pressure_valid = (oilp != 0xFFFFu);
328 out->oil_pressure_pa = (float)oilp * 100.0f;
329 uint16_t oilt = rd_u16le(payload + 3);
330 out->oil_temp_valid = (oilt != 0xFFFFu);
331 out->oil_temp_c = (float)oilt * 0.1f - 273.15f;
332 uint16_t clt = rd_u16le(payload + 5);
333 out->coolant_temp_valid = (clt != 0xFFFFu);
334 out->coolant_temp_c = (float)clt * 0.01f - 273.15f;
335 int16_t alt = rd_i16le(payload + 7);
336 out->alt_voltage_valid = ((uint16_t)alt != 0x7FFFu);
337 out->alt_voltage_v = (float)alt * 0.01f;
338 int16_t fr = rd_i16le(payload + 9);
339 out->fuel_rate_valid = ((uint16_t)fr != 0x7FFFu);
340 out->fuel_rate_lph = (float)fr * 0.1f;
341 uint32_t hrs = rd_u32le(payload + 11);
342 out->engine_hours_valid = (hrs != 0xFFFFFFFFu);
343 out->engine_hours_s = hrs;
344 uint16_t clp = rd_u16le(payload + 15);
345 out->coolant_pressure_valid = (clp != 0xFFFFu);
346 out->coolant_pressure_pa = (float)clp * 100.0f;
347 uint16_t fp = rd_u16le(payload + 17);
348 out->fuel_pressure_valid = (fp != 0xFFFFu);
349 out->fuel_pressure_pa = (float)fp * 1000.0f;
351 out->discrete_status_1 = rd_u16le(payload + 20);
352 out->discrete_status_2 = rd_u16le(payload + 22);
353 out->load_valid = (payload[24] != 0x7Fu);
354 out->load_pct = (int8_t)payload[24];
355 out->torque_valid = (payload[25] != 0x7Fu);
356 out->torque_pct = (int8_t)payload[25];
360bool pc_n2k_decode_wind_data(
const uint8_t *payload,
size_t len, N2kWindData *out)
362 if (!payload || !out || len < 6)
366 out->sid = payload[0];
367 uint16_t speed = rd_u16le(payload + 1);
368 uint16_t angle = rd_u16le(payload + 3);
369 out->speed_valid = (speed != 0xFFFFu);
370 out->speed_mps = (float)speed * 0.01f;
371 out->angle_valid = (angle != 0xFFFFu);
372 out->angle_rad = (float)angle * 0.0001f;
373 out->reference = (uint8_t)(payload[5] & 0x07u);
377bool pc_n2k_decode_speed(
const uint8_t *payload,
size_t len, N2kSpeed *out)
379 if (!payload || !out || len < 6)
383 out->sid = payload[0];
384 uint16_t w = rd_u16le(payload + 1);
385 out->water_valid = (w != 0xFFFFu);
386 out->water_mps = (float)w * 0.01f;
387 uint16_t g = rd_u16le(payload + 3);
388 out->ground_valid = (g != 0xFFFFu);
389 out->ground_mps = (float)g * 0.01f;
390 out->water_ref_type = payload[5];
394bool pc_n2k_decode_water_depth(
const uint8_t *payload,
size_t len, N2kWaterDepth *out)
396 if (!payload || !out || len < 7)
400 out->sid = payload[0];
401 uint32_t depth = rd_u32le(payload + 1);
402 out->depth_valid = (depth != 0xFFFFFFFFu);
403 out->depth_m = (float)depth * 0.01f;
404 out->offset_m = (float)rd_i16le(payload + 5) * 0.001f;
408bool pc_n2k_decode_vessel_heading(
const uint8_t *payload,
size_t len, N2kVesselHeading *out)
410 if (!payload || !out || len < 8)
414 out->sid = payload[0];
415 uint16_t heading = rd_u16le(payload + 1);
416 out->heading_valid = (heading != 0xFFFFu);
417 out->heading_rad = (float)heading * 0.0001f;
418 out->deviation_rad = (float)rd_i16le(payload + 3) * 0.0001f;
419 out->variation_rad = (float)rd_i16le(payload + 5) * 0.0001f;
420 out->reference = (uint8_t)(payload[7] & 0x03u);
#define PC_CAN_MAX_DLC
classic CAN carries at most 8 data octets.
NMEA 2000 codec (PC_ENABLE_NMEA2000) - the marine instrumentation network, built on J1939 over CAN.
uint8_t data[PC_CAN_MAX_DLC]