From 6b804f5ad7c1c035cc769e641e263ad3e5dd1f59 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Thu, 17 Sep 2026 06:55:56 -0400 Subject: [PATCH 1/4] firmware/j1939: fix J1939 NAME byte ordering per SAE J1939-81 and add fallback definitions --- firmware/RAMNV1/Core/Inc/ramn_j1939.h | 37 +++++++++++++++++++++++++-- firmware/RAMNV1/Core/Src/ramn_j1939.c | 28 +++++++++++++------- firmware/RAMNV1/Core/Src/ramn_xcp.c | 8 +++--- scripts/tests/test_j1939_protocol.py | 16 ++++++------ 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/firmware/RAMNV1/Core/Inc/ramn_j1939.h b/firmware/RAMNV1/Core/Inc/ramn_j1939.h index 410e61a0..220fc095 100644 --- a/firmware/RAMNV1/Core/Inc/ramn_j1939.h +++ b/firmware/RAMNV1/Core/Inc/ramn_j1939.h @@ -89,23 +89,56 @@ #define J1939_MANUFACTURER_NEBRASKA_TEST 522 #define J1939_MANUFACTURER_DLG_TEST 251 -/* Per-ECU primary source address for J1939 Address Claim */ +#define J1939_ECUA_IDENTITY 0xEC0A /* Identity number: EC0A */ +#define J1939_ECUB_IDENTITY 0xEC0B /* Identity number: EC0B */ +#define J1939_ECUC_IDENTITY 0xEC0C /* Identity number: EC0C */ +#define J1939_ECUD_IDENTITY 0xEC0D /* Identity number: EC0D */ + +/* Per-ECU primary source address for J1939 Address Claim and diagnostic routing */ #if defined(TARGET_ECUA) #define J1939_ECU_SA J1939_SA_HEADWAY_CTRL /* 42 */ #define J1939_ECU_FUNCTION J1939_FUNCTION_HEADWAY_CTRL /* 32 */ +#define J1939_ECU_IDENTITY J1939_ECUA_IDENTITY #define J1939_ECU_MANUFACTURER J1939_MANUFACTURER_NEVADA_TEST +#define J1939_ECU_KWP_SA J1939_ECU_SA +#define J1939_ECU_XCP_SA J1939_ECU_SA +#define J1939_ECU_KWP_XCP_SA J1939_ECU_SA #elif defined(TARGET_ECUB) #define J1939_ECU_SA J1939_SA_STEERING_CTRL /* 19 */ -#define J1939_ECU_FUNCTION J1939_FUNCTION_STEERING_CTRL /* 16 */ +#define J1939_ECU_FUNCTION J1939_FUNCTION_CHASSIS_CTRL /* 52: Chassis Controller */ +#define J1939_ECU_IDENTITY J1939_ECUB_IDENTITY #define J1939_ECU_MANUFACTURER J1939_MANUFACTURER_ISOBUS_TEST +#define J1939_ECU_KWP_SA J1939_ECU_SA +#define J1939_ECU_XCP_SA J1939_ECU_SA +#define J1939_ECU_KWP_XCP_SA J1939_ECU_SA #elif defined(TARGET_ECUC) #define J1939_ECU_SA J1939_SA_POWERTRAIN_CTRL /* 90 */ #define J1939_ECU_FUNCTION J1939_FUNCTION_ELECTRIC_POWERTRAIN /* 80 */ +#define J1939_ECU_IDENTITY J1939_ECUC_IDENTITY #define J1939_ECU_MANUFACTURER J1939_MANUFACTURER_NEBRASKA_TEST +#define J1939_ECU_KWP_SA J1939_ECU_SA +#define J1939_ECU_XCP_SA J1939_ECU_SA +#define J1939_ECU_KWP_XCP_SA J1939_ECU_SA #elif defined(TARGET_ECUD) #define J1939_ECU_SA J1939_SA_BODY_CTRL /* 33 */ #define J1939_ECU_FUNCTION J1939_FUNCTION_BODY_CTRL /* 26 */ +#define J1939_ECU_IDENTITY J1939_ECUD_IDENTITY +#define J1939_ECU_MANUFACTURER J1939_MANUFACTURER_DLG_TEST +#define J1939_ECU_KWP_SA J1939_ECU_SA +#define J1939_ECU_XCP_SA J1939_ECU_SA +#define J1939_ECU_KWP_XCP_SA J1939_ECU_SA +#else +/* Fallback definitions when no TARGET_ECUx macro is defined. + * This branch exists specifically for IDE code assistance / indexers, + * static analysis tools, and standalone / mock compilation units. + * Defaults safely to the primary ECU D (Body Controller) configuration. */ +#define J1939_ECU_SA J1939_SA_BODY_CTRL +#define J1939_ECU_FUNCTION J1939_FUNCTION_BODY_CTRL +#define J1939_ECU_IDENTITY J1939_ECUD_IDENTITY #define J1939_ECU_MANUFACTURER J1939_MANUFACTURER_DLG_TEST +#define J1939_ECU_KWP_SA J1939_ECU_SA +#define J1939_ECU_XCP_SA J1939_ECU_SA +#define J1939_ECU_KWP_XCP_SA J1939_ECU_SA #endif /* Proprietary A Payload Definitions */ diff --git a/firmware/RAMNV1/Core/Src/ramn_j1939.c b/firmware/RAMNV1/Core/Src/ramn_j1939.c index e09f9ba8..f72f7adc 100644 --- a/firmware/RAMNV1/Core/Src/ramn_j1939.c +++ b/firmware/RAMNV1/Core/Src/ramn_j1939.c @@ -33,30 +33,40 @@ static uint32_t J1939_MakeId(uint8_t prio, uint8_t pf, uint8_t da, uint8_t sa) } /* - * J1939 NAME (8 bytes). + * J1939 NAME (8 bytes, transmitted Least Significant Byte (LSB) first per SAE J1939-81). + * + * CAN Data Byte layout (Little-Endian / LSB first): + * Byte 0: Identity Number bits 7-0 + * Byte 1: Identity Number bits 15-8 + * Byte 2: Identity Number bits 20-16 (bits 4-0), Manufacturer Code bits 2-0 (bits 7-5) + * Byte 3: Manufacturer Code bits 10-3 + * Byte 4: ECU Instance bits 2-0 (bits 2-0), Function Instance bits 4-0 (bits 7-3) + * Byte 5: Function (bits 7-0) + * Byte 6: Reserved bit 48 (bit 0, must be 0), Vehicle System bits 6-0 (bits 7-1) + * Byte 7: Vehicle System Instance bits 3-0 (bits 3-0), Industry Group bits 2-0 (bits 6-4), Arbitrary Address Capable (bit 7) */ static const uint8_t j1939_name[8] = { #if defined(TARGET_ECUA) - 0x10, 0x02, J1939_ECU_FUNCTION, 0x00, 0x0F, 0xA0, 0x00, 0x01 /* Identity=1, MC=125, Function=32 (Headway Ctrl) */ + 0x0A, 0xEC, 0xA0, 0x0F, 0x00, J1939_ECU_FUNCTION, 0x02, 0x10 /* Identity=0xEC0A, MC=125, Function=32 (Headway Ctrl) */ #elif defined(TARGET_ECUB) - 0x10, 0x02, J1939_ECU_FUNCTION, 0x00, 0x3F, 0x60, 0x00, 0x02 /* Identity=2, MC=507, Function=16 (Steering Ctrl) */ + 0x0B, 0xEC, 0x60, 0x3F, 0x00, J1939_ECU_FUNCTION, 0x02, 0x10 /* Identity=0xEC0B, MC=507, Function=52 (Chassis Ctrl) */ #elif defined(TARGET_ECUC) - 0x10, 0x02, J1939_ECU_FUNCTION, 0x00, 0x41, 0x40, 0x00, 0x03 /* Identity=3, MC=522, Function=80 (Powertrain Ctrl) */ + 0x0C, 0xEC, 0x40, 0x41, 0x00, J1939_ECU_FUNCTION, 0x02, 0x10 /* Identity=0xEC0C, MC=522, Function=80 (Powertrain Ctrl) */ #elif defined(TARGET_ECUD) - 0x10, 0x02, J1939_ECU_FUNCTION, 0x00, 0x1F, 0x60, 0x00, 0x04 /* Identity=4, MC=251, Function=26 (Body Ctrl) */ + 0x0D, 0xEC, 0x60, 0x1F, 0x00, J1939_ECU_FUNCTION, 0x02, 0x10 /* Identity=0xEC0D, MC=251, Function=26 (Body Ctrl) */ #endif }; /* ECU Identification string for PGN 64965 (fields delimited by '*') */ static const char j1939_ecu_id[] = #if defined(TARGET_ECUA) - "RAMN*ECU_A*0001*UNIT1*"; + "RAMN*ECU_A*EC0A*UNIT1*"; #elif defined(TARGET_ECUB) - "RAMN*ECU_B*0002*UNIT2*"; + "RAMN*ECU_B*EC0B*UNIT2*"; #elif defined(TARGET_ECUC) - "RAMN*ECU_C*0003*UNIT3*"; + "RAMN*ECU_C*EC0C*UNIT3*"; #elif defined(TARGET_ECUD) - "RAMN*ECU_D*0004*UNIT4*"; + "RAMN*ECU_D*EC0D*UNIT4*"; #else "RAMN*ECU_UNKNOWN*"; #endif diff --git a/firmware/RAMNV1/Core/Src/ramn_xcp.c b/firmware/RAMNV1/Core/Src/ramn_xcp.c index 0a4bdcd1..c6db9c2b 100644 --- a/firmware/RAMNV1/Core/Src/ramn_xcp.c +++ b/firmware/RAMNV1/Core/Src/ramn_xcp.c @@ -409,12 +409,12 @@ RAMN_Bool_t RAMN_XCP_ProcessRxCANMessage(const FDCAN_RxHeaderTypeDef* pHeader, c // Accept XCP over both addressing schemes regardless of the live traffic profile: // J1939 proprietary-A (PF 0xEF, physical, TSA 0x3F or 0x5A) or standard 11-bit XCP_RX_CANID. - if (pHeader->IdType == FDCAN_EXTENDED_ID && pf == 0xEF && da == J1939_ECU_SA) + if (pHeader->IdType == FDCAN_EXTENDED_ID && pf == 0xEF && da == J1939_ECU_XCP_SA) { - // Avoid ECUC (SA 0x5A) responding to itself if another ECU is using TSA 0x5A - if ((sa == 0x3F || sa == 0x5A) && (sa != J1939_ECU_SA)) + // Avoid responding to self if tester uses same SA + if ((sa == 0x3F || sa == 0x5A) && (sa != J1939_ECU_XCP_SA)) { - RAMN_XCP_TxMsgHeader.Identifier = J1939_UCAST_ID(prio, 0xEF00, sa, J1939_ECU_SA); + RAMN_XCP_TxMsgHeader.Identifier = J1939_UCAST_ID(prio, 0xEF00, sa, J1939_ECU_XCP_SA); RAMN_XCP_TxMsgHeader.IdType = FDCAN_EXTENDED_ID; matched = True; } diff --git a/scripts/tests/test_j1939_protocol.py b/scripts/tests/test_j1939_protocol.py index 46d6e215..b5e7a60c 100644 --- a/scripts/tests/test_j1939_protocol.py +++ b/scripts/tests/test_j1939_protocol.py @@ -49,17 +49,17 @@ # J1939 NAME per ECU: (function, identity_number) — all other fields zero ECU_NAME_FIELDS = { - 'A': {'function': 42, 'identity': 1}, - 'B': {'function': 19, 'identity': 2}, - 'C': {'function': 90, 'identity': 3}, - 'D': {'function': 33, 'identity': 4}, + 'A': {'function': 42, 'identity': 0xEC0A}, + 'B': {'function': 52, 'identity': 0xEC0B}, + 'C': {'function': 90, 'identity': 0xEC0C}, + 'D': {'function': 33, 'identity': 0xEC0D}, } ECU_ID_STRINGS = { - 'A': "RAMN*ECU_A*0001*UNIT1*", - 'B': "RAMN*ECU_B*0002*UNIT2*", - 'C': "RAMN*ECU_C*0003*UNIT3*", - 'D': "RAMN*ECU_D*0004*UNIT4*", + 'A': "RAMN*ECU_A*EC0A*UNIT1*", + 'B': "RAMN*ECU_B*EC0B*UNIT2*", + 'C': "RAMN*ECU_C*EC0C*UNIT3*", + 'D': "RAMN*ECU_D*EC0D*UNIT4*", } # --------------------------------------------------------------------------- From 5b2dc57a62552c4b246804a59b41413fc81c417b Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Thu, 17 Sep 2026 06:56:12 -0400 Subject: [PATCH 2/4] tests/mocks: add ISO-TP multi-frame flow control pump to KWP mock --- scripts/tests/mocks/ramn_diag_mocks.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/tests/mocks/ramn_diag_mocks.c b/scripts/tests/mocks/ramn_diag_mocks.c index c231ea7f..b5825240 100644 --- a/scripts/tests/mocks/ramn_diag_mocks.c +++ b/scripts/tests/mocks/ramn_diag_mocks.c @@ -137,6 +137,15 @@ size_t xStreamBufferSend(StreamBufferHandle_t xStreamBuffer, const void *pvTxDat RAMN_KWP_ISOTPHandler.txSize = ans_size; RAMN_KWP_ISOTPHandler.txStatus = ISOTP_TX_TRANSFERRING; RAMN_KWP_Continue_TX(0); + // Complete any multi-frame transfer the same way a tester would: after the + // FirstFrame, feed a FlowControl "Continue To Send" and pump ConsecutiveFrames. + for (int guard = 0; (RAMN_KWP_ISOTPHandler.txStatus != ISOTP_TX_IDLE) && (guard < 8192); guard++) { + if (RAMN_KWP_ISOTPHandler.txStatus == ISOTP_TX_WAITING_FLAG) { + uint8_t fc[3] = {0x30, 0x00, 0x00}; // FlowControl: CTS, BS=0, STmin=0 + RAMN_ISOTP_ProcessRxMsg(&RAMN_KWP_ISOTPHandler, 3, fc, False, 0); + } + RAMN_KWP_Continue_TX(0); + } } kwp_expected = 0; } From 77c24e5be5484ec93eb15c8739a69394fbc448f5 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Thu, 17 Sep 2026 06:56:24 -0400 Subject: [PATCH 3/4] firmware/kwp2000: define standard ISO 14230-3 Negative Response Codes --- firmware/RAMNV1/Core/Src/ramn_kwp2000.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/firmware/RAMNV1/Core/Src/ramn_kwp2000.c b/firmware/RAMNV1/Core/Src/ramn_kwp2000.c index 8ff97076..9a837332 100644 --- a/firmware/RAMNV1/Core/Src/ramn_kwp2000.c +++ b/firmware/RAMNV1/Core/Src/ramn_kwp2000.c @@ -51,9 +51,17 @@ #define KWP_COMMAND_CONTROL_DTC_SETTINGS 0x85 #define KWP_COMMAND_RESPONSE_ON_EVENT 0x86 -// KWP Errors +// KWP Errors (ISO 14230-3) #define KWP_NRC_SERVICE_NOT_SUPPORTED 0x11 #define KWP_NRC_SUBFUNCTION_NOT_SUPPORTED_IF 0x12 +#define KWP_NRC_INCORRECT_MESSAGE_LENGTH 0x13 +#define KWP_NRC_CONDITIONS_NOT_CORRECT 0x22 +#define KWP_NRC_REQUEST_SEQUENCE_ERROR 0x24 +#define KWP_NRC_REQUEST_OUT_OF_RANGE 0x31 +#define KWP_NRC_SECURITY_ACCESS_DENIED 0x33 +#define KWP_NRC_INVALID_KEY 0x35 +#define KWP_NRC_EXCEED_NUMBER_OF_ATTEMPTS 0x36 +#define KWP_NRC_REQUIRED_TIME_DELAY_NOT_EXPIRED 0x37 // TODO: refactor module // Common pointer to avoid passing answer data as argument each sub-function From 68d87866837c58b52f79d812253020359e4c4e86 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Thu, 17 Sep 2026 06:59:36 -0400 Subject: [PATCH 4/4] tests/build: include stddef.h and string.h for serdes library build --- scripts/tests/build_testing_libs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests/build_testing_libs.sh b/scripts/tests/build_testing_libs.sh index 12325d0b..d420a40d 100755 --- a/scripts/tests/build_testing_libs.sh +++ b/scripts/tests/build_testing_libs.sh @@ -28,7 +28,7 @@ touch scripts/tests/mocks/eeprom_emul_conf.h echo "Compiling Serdes Shared Library..." # Both codecs (RAMN_*_Default and RAMN_*_J1939) live in the single library; tests pick by suffix. -gcc $CFLAGS -D__MAIN_H $INCLUDES -o scripts/tests/librbd_can_db.so firmware/RAMNV1/Core/Src/ramn_can_database.c +gcc $CFLAGS -D__MAIN_H -include stddef.h -include string.h $INCLUDES -o scripts/tests/librbd_can_db.so firmware/RAMNV1/Core/Src/ramn_can_database.c echo "Compiling ECU-specific Diagnostic Shared Libraries..."