15bool pc_j1939_encode_id(uint32_t *
id, uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da)
17 if (!
id || priority > 7 || pgn > 0x3FFFFu)
21 uint8_t edp = (uint8_t)((pgn >> 17) & 1u);
22 uint8_t dp = (uint8_t)((pgn >> 16) & 1u);
23 uint8_t pf = (uint8_t)((pgn >> 8) & 0xFFu);
26 uint8_t ps = (pf < J1939_PDU2_THRESHOLD) ? da : (uint8_t)(pgn & 0xFFu);
27 *
id = ((uint32_t)(priority & 7u) << 26) | ((uint32_t)edp << 25) | ((uint32_t)dp << 24) | ((uint32_t)pf << 16) |
28 ((uint32_t)ps << 8) | (uint32_t)sa;
32bool pc_j1939_decode_id(uint32_t
id, J1939Id *out)
39 out->priority = (uint8_t)((
id >> 26) & 7u);
40 uint8_t edp = (uint8_t)((
id >> 25) & 1u);
41 uint8_t dp = (uint8_t)((
id >> 24) & 1u);
42 out->pf = (uint8_t)((
id >> 16) & 0xFFu);
43 out->ps = (uint8_t)((
id >> 8) & 0xFFu);
44 out->sa = (uint8_t)(
id & 0xFFu);
45 out->pdu1 = out->pf < J1939_PDU2_THRESHOLD;
49 out->pgn = ((uint32_t)edp << 17) | ((uint32_t)dp << 16) | ((uint32_t)out->pf << 8);
53 out->da = J1939_ADDR_GLOBAL;
54 out->pgn = ((uint32_t)edp << 17) | ((uint32_t)dp << 16) | ((uint32_t)out->pf << 8) | out->ps;
60static bool ext_frame(
CanFrame *f, uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da, uint8_t dlc)
63 if (!pc_j1939_encode_id(&
id, priority, pgn, sa, da))
71 memset(f->
data, 0xFF,
sizeof(f->
data));
75bool pc_j1939_build_message(
CanFrame *out, uint8_t priority, uint32_t pgn, uint8_t sa, uint8_t da,
const uint8_t *data,
82 if (!ext_frame(out, priority, pgn, sa, da, len))
88 memcpy(out->
data, data, len);
93bool pc_j1939_build_request(
CanFrame *out, uint8_t sa, uint8_t da, uint32_t requested_pgn)
95 if (!out || requested_pgn > 0x3FFFFu)
100 if (!ext_frame(out, 6, J1939_PGN_REQUEST, sa, da, 3))
106 out->
data[0] = (uint8_t)requested_pgn;
107 out->
data[1] = (uint8_t)(requested_pgn >> 8);
108 out->
data[2] = (uint8_t)(requested_pgn >> 16);
112uint64_t pc_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)
121 n |= (uint64_t)(identity_number & 0x1FFFFFu);
122 n |= (uint64_t)(manufacturer_code & 0x7FFu) << 21;
123 n |= (uint64_t)(ecu_instance & 0x7u) << 32;
124 n |= (uint64_t)(function_instance & 0x1Fu) << 35;
125 n |= (uint64_t)(function & 0xFFu) << 40;
126 n |= (uint64_t)(vehicle_system & 0x7Fu) << 49;
127 n |= (uint64_t)(vehicle_system_instance & 0xFu) << 56;
128 n |= (uint64_t)(industry_group & 0x7u) << 60;
129 n |= (uint64_t)(arbitrary_address_capable ? 1u : 0u) << 63;
133bool pc_j1939_build_address_claim(
CanFrame *out, uint8_t sa, uint64_t name)
136 if (!ext_frame(out, 6, J1939_PGN_ADDRESS_CLAIM, sa, J1939_ADDR_GLOBAL, 8))
143 for (
int i = 0; i < 8; i++)
145 out->
data[i] = (uint8_t)(name >> (8 * i));
150uint8_t pc_j1939_tp_num_packets(uint16_t total_size)
152 return (uint8_t)((total_size + (J1939_TP_DT_LEN - 1)) / J1939_TP_DT_LEN);
155bool pc_j1939_build_bam_cm(
CanFrame *out, uint8_t sa, uint32_t pgn, uint16_t total_size)
157 if (!out || total_size < 9 || total_size > PC_J1939_TP_MAX || pgn > 0x3FFFFu)
161 if (!ext_frame(out, 7, J1939_PGN_TP_CM, sa, J1939_ADDR_GLOBAL, 8))
167 out->
data[0] = J1939_TP_CM_BAM;
168 out->
data[1] = (uint8_t)total_size;
169 out->
data[2] = (uint8_t)(total_size >> 8);
170 out->
data[3] = pc_j1939_tp_num_packets(total_size);
172 out->
data[5] = (uint8_t)pgn;
173 out->
data[6] = (uint8_t)(pgn >> 8);
174 out->
data[7] = (uint8_t)(pgn >> 16);
178bool pc_j1939_build_tp_dt(
CanFrame *out, uint8_t sa, uint8_t da, uint8_t seq,
const uint8_t *chunk, uint8_t chunk_len)
180 if (!out || seq == 0 || chunk_len == 0 || chunk_len > J1939_TP_DT_LEN || !chunk)
184 if (!ext_frame(out, 7, J1939_PGN_TP_DT, sa, da, 8))
190 memcpy(out->
data + 1, chunk, chunk_len);
194void pc_j1939_tp_reset(J1939TpRx *rx)
198 memset(rx, 0,
sizeof(*rx));
202J1939TpResult pc_j1939_tp_feed(J1939TpRx *rx,
const CanFrame *f)
206 return J1939TpResult::J1939_TP_IGNORED;
209 if (!pc_j1939_decode_id(f->
id, &
id))
212 return J1939TpResult::J1939_TP_IGNORED;
216 if (
id.pgn == J1939_PGN_TP_CM && f->
dlc >= 8)
218 uint8_t control = f->
data[0];
219 if (control != J1939_TP_CM_BAM && control != J1939_TP_CM_RTS)
221 return J1939TpResult::J1939_TP_IGNORED;
223 uint16_t total = (uint16_t)(f->
data[1] | (f->
data[2] << 8));
224 uint8_t packets = f->
data[3];
225 uint32_t pgn = (uint32_t)f->
data[5] | ((uint32_t)f->
data[6] << 8) | ((uint32_t)f->
data[7] << 16);
226 if (total < 9 || total > PC_J1939_TP_MAX || packets != pc_j1939_tp_num_packets(total))
228 return J1939TpResult::J1939_TP_ERROR;
233 rx->total_size = total;
234 rx->num_packets = packets;
237 return J1939TpResult::J1939_TP_STARTED;
240 if (
id.pgn == J1939_PGN_TP_DT && f->
dlc >= 1)
242 if (!rx->active ||
id.sa != rx->sa)
244 return J1939TpResult::J1939_TP_IGNORED;
246 uint8_t seq = f->
data[0];
247 if (seq != rx->next_seq)
249 pc_j1939_tp_reset(rx);
250 return J1939TpResult::J1939_TP_ERROR;
252 uint16_t remaining = (uint16_t)(rx->total_size - rx->received);
253 uint8_t take = remaining < J1939_TP_DT_LEN ? (uint8_t)remaining : (uint8_t)J1939_TP_DT_LEN;
254 memcpy(rx->buf + rx->received, f->
data + 1, take);
255 rx->received = (uint16_t)(rx->received + take);
257 if (rx->received >= rx->total_size)
260 return J1939TpResult::J1939_TP_COMPLETE;
262 return J1939TpResult::J1939_TP_PROGRESS;
265 return J1939TpResult::J1939_TP_IGNORED;
270bool pc_j1939_decode_eec1(
const CanFrame *f, J1939Eec1 *out)
272 if (!f || !out || f->
dlc < 8)
277 if (!pc_j1939_decode_id(f->
id, &
id) ||
id.pgn != J1939_PGN_EEC1)
281 out->torque_mode = (uint8_t)(f->
data[0] & 0x0Fu);
283 out->drivers_demand_torque_pct = (f->
data[1] <= 0xFAu) ? (int16_t)((int)f->
data[1] - 125) : J1939_TORQUE_NA;
284 out->actual_engine_torque_pct = (f->
data[2] <= 0xFAu) ? (int16_t)((int)f->
data[2] - 125) : J1939_TORQUE_NA;
285 uint16_t raw = (uint16_t)(f->
data[3] | ((uint16_t)f->
data[4] << 8));
286 out->engine_speed_valid = (raw <= 0xFAFFu);
287 out->engine_speed_rpm = (float)raw * 0.125f;
291bool pc_j1939_decode_et1(
const CanFrame *f, J1939Et1 *out)
293 if (!f || !out || f->
dlc < 8)
298 if (!pc_j1939_decode_id(f->
id, &
id) ||
id.pgn != J1939_PGN_ET1)
302 out->coolant_valid = (f->
data[0] <= 0xFAu);
303 out->coolant_temp_c = (float)((
int)f->
data[0] - 40);
304 out->fuel_valid = (f->
data[1] <= 0xFAu);
305 out->fuel_temp_c = (float)((
int)f->
data[1] - 40);
306 uint16_t oilraw = (uint16_t)(f->
data[2] | ((uint16_t)f->
data[3] << 8));
307 out->oil_valid = (oilraw <= 0xFAFFu);
308 out->oil_temp_c = (float)oilraw * 0.03125f - 273.0f;
312bool pc_j1939_decode_lfe(
const CanFrame *f, J1939Lfe *out)
314 if (!f || !out || f->
dlc < 8)
319 if (!pc_j1939_decode_id(f->
id, &
id) ||
id.pgn != J1939_PGN_LFE)
323 uint16_t fr = (uint16_t)(f->
data[0] | ((uint16_t)f->
data[1] << 8));
324 out->fuel_rate_valid = (fr <= 0xFAFFu);
325 out->fuel_rate_lph = (float)fr * 0.05f;
326 uint16_t ie = (uint16_t)(f->
data[2] | ((uint16_t)f->
data[3] << 8));
327 out->instant_econ_valid = (ie <= 0xFAFFu);
328 out->instant_econ_kmpl = (float)ie * (1.0f / 512.0f);
329 uint16_t ae = (uint16_t)(f->
data[4] | ((uint16_t)f->
data[5] << 8));
330 out->avg_econ_valid = (ae <= 0xFAFFu);
331 out->avg_econ_kmpl = (float)ae * (1.0f / 512.0f);
332 out->throttle_valid = (f->
data[6] <= 0xFAu);
333 out->throttle_pct = (float)f->
data[6] * 0.4f;
337bool pc_j1939_decode_amb(
const CanFrame *f, J1939Amb *out)
339 if (!f || !out || f->
dlc < 8)
344 if (!pc_j1939_decode_id(f->
id, &
id) ||
id.pgn != J1939_PGN_AMB)
348 out->baro_valid = (f->
data[0] <= 0xFAu);
349 out->baro_kpa = (float)f->
data[0] * 0.5f;
350 uint16_t cab = (uint16_t)(f->
data[1] | ((uint16_t)f->
data[2] << 8));
351 out->cab_temp_valid = (cab <= 0xFAFFu);
352 out->cab_temp_c = (float)cab * 0.03125f - 273.0f;
353 uint16_t amb = (uint16_t)(f->
data[3] | ((uint16_t)f->
data[4] << 8));
354 out->ambient_temp_valid = (amb <= 0xFAFFu);
355 out->ambient_temp_c = (float)amb * 0.03125f - 273.0f;
356 out->inlet_temp_valid = (f->
data[5] <= 0xFAu);
357 out->inlet_temp_c = (float)((
int)f->
data[5] - 40);
358 uint16_t road = (uint16_t)(f->
data[6] | ((uint16_t)f->
data[7] << 8));
359 out->road_temp_valid = (road <= 0xFAFFu);
360 out->road_temp_c = (float)road * 0.03125f - 273.0f;
364bool pc_j1939_decode_ic1(
const CanFrame *f, J1939Ic1 *out)
366 if (!f || !out || f->
dlc < 8)
371 if (!pc_j1939_decode_id(f->
id, &
id) ||
id.pgn != J1939_PGN_IC1)
375 out->trap_inlet_valid = (f->
data[0] <= 0xFAu);
376 out->trap_inlet_kpa = (float)f->
data[0] * 0.5f;
377 out->boost_valid = (f->
data[1] <= 0xFAu);
378 out->boost_kpa = (float)f->
data[1] * 2.0f;
379 out->intake_temp_valid = (f->
data[2] <= 0xFAu);
380 out->intake_temp_c = (float)((
int)f->
data[2] - 40);
381 out->air_inlet_valid = (f->
data[3] <= 0xFAu);
382 out->air_inlet_kpa = (float)f->
data[3] * 2.0f;
383 out->air_filter_valid = (f->
data[4] <= 0xFAu);
384 out->air_filter_kpa = (float)f->
data[4] * 0.05f;
385 uint16_t egt = (uint16_t)(f->
data[5] | ((uint16_t)f->
data[6] << 8));
386 out->exhaust_temp_valid = (egt <= 0xFAFFu);
387 out->exhaust_temp_c = (float)egt * 0.03125f - 273.0f;
388 out->coolant_filter_valid = (f->
data[7] <= 0xFAu);
389 out->coolant_filter_kpa = (float)f->
data[7] * 0.5f;
393bool pc_j1939_decode_vd(
const CanFrame *f, J1939Vd *out)
395 if (!f || !out || f->
dlc < 8)
400 if (!pc_j1939_decode_id(f->
id, &
id) ||
id.pgn != J1939_PGN_VD)
405 uint32_t trip = (uint32_t)f->
data[0] | ((uint32_t)f->
data[1] << 8) | ((uint32_t)f->
data[2] << 16) |
406 ((uint32_t)f->
data[3] << 24);
407 out->trip_valid = (trip <= 0xFAFFFFFFu);
408 out->trip_km = (double)trip * 0.125;
409 uint32_t total = (uint32_t)f->
data[4] | ((uint32_t)f->
data[5] << 8) | ((uint32_t)f->
data[6] << 16) |
410 ((uint32_t)f->
data[7] << 24);
411 out->total_valid = (total <= 0xFAFFFFFFu);
412 out->total_km = (double)total * 0.125;
416bool pc_j1939_decode_ccvs(
const CanFrame *f, J1939Ccvs *out)
418 if (!f || !out || f->
dlc < 8)
423 if (!pc_j1939_decode_id(f->
id, &
id) ||
id.pgn != J1939_PGN_CCVS)
428 uint16_t ws = (uint16_t)(f->
data[1] | ((uint16_t)f->
data[2] << 8));
429 out->speed_valid = (ws <= 0xFAFFu);
430 out->wheel_speed_kmh = (float)ws * (1.0f / 256.0f);
432 out->cruise_active = (uint8_t)(f->
data[3] & 0x03u);
436bool pc_j1939_decode_dm1(
const uint8_t *body,
size_t len, J1939Dm1 *out, J1939Dtc *out_dtcs,
size_t max)
438 if (!body || !out || len < 2)
443 out->protect = (uint8_t)(body[0] & 0x03u);
444 out->amber_warning = (uint8_t)((body[0] >> 2) & 0x03u);
445 out->red_stop = (uint8_t)((body[0] >> 4) & 0x03u);
446 out->mil = (uint8_t)((body[0] >> 6) & 0x03u);
448 size_t ndtc = (len - 2) / 4;
450 for (
size_t i = 0; i < ndtc; i++)
456 const uint8_t *d = body + 2 + i * 4;
457 uint32_t spn = (uint32_t)d[0] | ((uint32_t)d[1] << 8) | (((uint32_t)d[2] >> 5) << 16);
458 uint8_t fmi = (uint8_t)(d[2] & 0x1Fu);
459 if (spn == 0 && fmi == 0)
465 out_dtcs[stored].spn = spn;
466 out_dtcs[stored].fmi = fmi;
467 out_dtcs[stored].cm = (uint8_t)((d[3] >> 7) & 0x01u);
468 out_dtcs[stored].oc = (uint8_t)(d[3] & 0x7Fu);
472 out->dtc_count = stored;
#define PC_CAN_MAX_DLC
classic CAN carries at most 8 data octets.
#define PC_CAN_EXT_ID_MASK
29-bit extended identifier.
SAE J1939 message codec (PC_ENABLE_J1939) - the heavy-duty-vehicle / agriculture / marine / genset CA...
uint8_t data[PC_CAN_MAX_DLC]