uds:   ISO 14229 (Unified Diagnostic Services) in pure Racket
1 Quick start
2 Service identifiers
sid:  diagnostic-session-control
sid:  ecu-reset
sid:  clear-diagnostic-information
sid:  read-dtc-information
sid:  read-data-by-identifier
sid:  read-memory-by-address
sid:  read-scaling-data-by-identifier
sid:  security-access
sid:  authentication
sid:  communication-control
sid:  read-data-by-periodic-identifier
sid:  dynamically-define-data-identifier
sid:  define-data-by-identifier
sid:  write-data-by-identifier
sid:  input-output-control-by-identifier
sid:  routine-control
sid:  request-download
sid:  request-upload
sid:  transfer-data
sid:  request-transfer-exit
sid:  request-file-transfer
sid:  write-memory-by-address
sid:  tester-present
sid:  access-timing-parameter
sid:  secured-data-transmission
sid:  control-dtc-setting
sid:  response-on-event
sid:  link-control
service-name
service-uses-sub-function?
positive-sid
response-sid->request-sid
make-sub-function-byte
parse-sub-function-byte
3 Negative response codes
negative-response
negative-response-parse
negative-response-bytes
nrc-name
response-pending?
nrc:  general-reject
nrc:  service-not-supported
nrc:  sub-function-not-supported
nrc:  incorrect-message-length-or-invalid-format
nrc:  response-too-long
nrc:  busy-repeat-request
nrc:  conditions-not-correct
nrc:  request-sequence-error
nrc:  no-response-from-subnet-component
nrc:  failure-prevents-execution-of-requested-action
nrc:  request-out-of-range
nrc:  security-access-denied
nrc:  authentication-required
nrc:  invalid-key
nrc:  exceed-number-of-attempts
nrc:  required-time-delay-not-expired
nrc:  secure-data-transmission-required
nrc:  secure-data-transmission-not-allowed
nrc:  secure-data-verification-failed
nrc:  ownership-verification-failed
nrc:  challenge-calculation-failed
nrc:  setting-access-rights-failed
nrc:  session-key-creation-derivation-failed
nrc:  configuration-data-usage-failed
nrc:  de-authentication-failed
nrc:  upload-download-not-accepted
nrc:  transfer-data-suspended
nrc:  general-programming-failure
nrc:  wrong-block-sequence-counter
nrc:  request-correctly-received-response-pending
nrc:  sub-function-not-supported-in-active-session
nrc:  service-not-supported-in-active-session
nrc:  rpm-too-high
nrc:  rpm-too-low
nrc:  engine-is-running
nrc:  engine-is-not-running
nrc:  engine-run-time-too-low
nrc:  temperature-too-high
nrc:  temperature-too-low
nrc:  vehicle-speed-too-high
nrc:  vehicle-speed-too-low
nrc:  throttle-pedal-too-high
nrc:  throttle-pedal-too-low
nrc:  transmission-range-not-in-neutral
nrc:  transmission-range-not-in-gear
nrc:  brake-switch-not-closed
nrc:  shifter-lever-not-in-park
nrc:  torque-converter-clutch-locked
nrc:  voltage-too-high
nrc:  voltage-too-low
nrc:  resource-temporarily-not-available
4 Diagnostic trouble codes
dtc
dtc-status
dtc-text->number
dtc-number->text
parse-dtc-records
make-dtc-status
dtc-status->byte
byte->dtc-status
+  dtc-group-all+
5 Requests
request
request-parse
make-request
diagnostic-session-control
ecu-reset
tester-present
control-dtc-setting
clear-diagnostic-information
read-dtc-by-status-mask
report-number-of-dtc-by-status-mask
report-supported-dtc
security-access-request-seed
security-access-send-key
read-data-by-identifier
write-data-by-identifier
input-output-control-by-identifier
routine-control
read-memory-by-address
write-memory-by-address
request-download
transfer-data
request-transfer-exit
encode-address-and-length
decode-address-and-length
6 Responses
response-parse
positive-response
session-control-response
security-seed-response
max-block-length-response
number-of-dtc-response
dtc-list-response
transfer-data-response
7 Caveats
9.4.0.2

uds: ISO 14229 (Unified Diagnostic Services) in pure Racket🔗ℹ

 (require uds) package: uds

This package implements the message layer of ISO 14229-1 — UDS (Unified Diagnostic Services), the application protocol spoken by automotive ECUs over ISO-TP (isotp) or DoIP. It covers service identifiers, negative response codes, diagnostic trouble codes, and request/response construction and parsing.

The library is transport-agnostic and pure: builders return the payload bytes of one ISO-TP message and parsers consume them; timing, transport and security-algorithm concerns stay with the caller. There is no FFI and no system dependency.

1 Quick start🔗ℹ

(require uds)
 
 
(diagnostic-session-control 'extended)
 
 
(read-data-by-identifier '(61840))
 
 
(response-parse #"P\3\0002\0\310")
(session-control-response (positive-response-data (response-parse #"P\3\0002\0\310")))

2 Service identifiers🔗ℹ

The sid:* bindings name every ISO 14229-1 service (sid:diagnostic-session-control, sid:read-data-by-identifier, …). service-name maps an ID back to a symbol, and service-uses-sub-function? tells whether the service’s second byte is a sub-function — the distinction that drives generic framing.

The full service-identifier table, as exported constants:

value

sid:ecu-reset : (integer-in 0 255)

value

sid:security-access : (integer-in 0 255)

value

sid:authentication : (integer-in 0 255)

value

sid:routine-control : (integer-in 0 255)

value

sid:request-download : (integer-in 0 255)

value

sid:request-upload : (integer-in 0 255)

value

sid:transfer-data : (integer-in 0 255)

value

sid:tester-present : (integer-in 0 255)

value

sid:link-control : (integer-in 0 255)

procedure

(service-name sid) → (or/c symbol? #f)

  sid : (integer-in 0 255)
The ISO name of the service as a symbol (e.g. 'read-data-by-identifier), or #f for codes outside the table.

procedure

(service-uses-sub-function? sid) → boolean?

  sid : (integer-in 0 255)
Does this service carry a sub-function byte? The distinction that drives the generic framing in make-request and request-parse.

procedure

(positive-sid sid) → (integer-in 0 255)

  sid : (integer-in 0 255)
The positive-response marker: sid | 64.

procedure

(response-sid->request-sid sid) → (integer-in 0 255)

  sid : (integer-in 0 255)
Inverse of positive-sid (clears the marker bit).

procedure

(make-sub-function-byte sub suppress?) → (integer-in 0 255)

  sub : (integer-in 0 127)
  suppress? : boolean?

procedure

(parse-sub-function-byte b) → 
(integer-in 0 127) boolean?
  b : (integer-in 0 255)
Encode/decode the sub-function byte: bits 0–6 carry the sub-function, bit 7 is suppressPosRspMsgIndicationBit.

3 Negative response codes🔗ℹ

Negative responses are framed as 7F <echoed SID> <NRC>.

struct

(struct negative-response (sid nrc))

  sid : (integer-in 0 255)
  nrc : (integer-in 0 255)
The parsed negative response. negative-response-parse and negative-response-bytes convert to and from wire bytes; nrc-name maps a code to a symbol (nrc:request-out-of-range, nrc:security-access-denied, …); response-pending? recognizes the requestCorrectlyReceived-ResponsePending code (0x78) that tells the tester to keep waiting.

procedure

(negative-response-parse payload) → negative-response?

  payload : bytes?
Parses a 7F <SID> <NRC> payload; raises on anything else.

procedure

(negative-response-bytes sid nrc) → bytes?

  sid : (integer-in 0 255)
  nrc : (integer-in 0 255)
Builds the negative-response payload.

procedure

(nrc-name nrc) → (or/c symbol? #f)

  nrc : (integer-in 0 255)
The ISO name of the negative response code as a symbol (SDF naming: 'requestOutOfRange, 'securityAccessDenied, ...), or #f when the code is not in the table.

procedure

(response-pending? payload) → boolean?

  payload : bytes?
Recognizes the requestCorrectlyReceived-ResponsePending negative response (NRC 0x78) that tells the tester to keep waiting.

The exported NRC constants:

value

nrc:general-reject : (integer-in 0 255)

value

nrc:invalid-key : (integer-in 0 255)

value

nrc:rpm-too-high : (integer-in 0 255)

value

nrc:rpm-too-low : (integer-in 0 255)

value

nrc:voltage-too-high : (integer-in 0 255)

value

nrc:voltage-too-low : (integer-in 0 255)

4 Diagnostic trouble codes🔗ℹ

struct

(struct dtc (number status-byte))

  number : (integer-in 0 65535)
  status-byte : (integer-in 0 255)
One trouble code: a 16-bit number and a status byte. The number’s text form ("P0123") maps through dtc-text->number / dtc-number->text: bits 15–14 encode the system letter (P/C/B/U), bits 13–12 the first digit, and the remaining three digits occupy one BCD nibble each. parse-dtc-records reads the 3-byte record runs of ReadDTCInformation responses.

struct

(struct dtc-status (test-failed
    test-failed-this-operation-cycle
    pending
    confirmed
    test-not-completed-since-last-clear
    test-failed-since-last-clear
    test-not-completed-this-operation-cycle
    warning-indicator-requested))
  test-failed : boolean?
  test-failed-this-operation-cycle : boolean?
  pending : boolean?
  confirmed : boolean?
  test-not-completed-since-last-clear : boolean?
  test-failed-since-last-clear : boolean?
  test-not-completed-this-operation-cycle : boolean?
  warning-indicator-requested : boolean?
The status byte (ISO 14229-1 Annex D) as flags; make-dtc-status, dtc-status->byte and byte->dtc-status convert. +dtc-group-all+ (0xFFFFFF) is the "all DTCs" group for ClearDiagnosticInformation.

procedure

(dtc-text->number s) → (integer-in 0 65535)

  s : string?
Parses the three-letter text form ("P0123") into the 16-bit number.

procedure

(dtc-number->text n) → string?

  n : (integer-in 0 65535)
Renders the 16-bit number as the three-letter text form.

procedure

(parse-dtc-records payload) → (listof dtc?)

  payload : bytes?
Reads the run of 3-byte [number_hi number_lo status] records that ReadDTCInformation responses carry.

procedure

(make-dtc-status 
  [#:test-failed test-failed 
  #:test-failed-this-operation-cycle test-failed-this-operation-cycle 
  #:pending pending 
  #:confirmed confirmed 
  #:test-not-completed-since-last-clear test-not-completed-since-last-clear 
  #:test-failed-since-last-clear test-failed-since-last-clear 
  #:test-not-completed-this-operation-cycle test-not-completed-this-operation-cycle 
  #:warning-indicator-requested warning-indicator-requested]) 
 → dtc-status?
  test-failed : boolean? = #f
  test-failed-this-operation-cycle : boolean? = #f
  pending : boolean? = #f
  confirmed : boolean? = #f
  test-not-completed-since-last-clear : boolean? = #f
  test-failed-since-last-clear : boolean? = #f
  test-not-completed-this-operation-cycle : boolean? = #f
  warning-indicator-requested : boolean? = #f
Builds a status byte by keyword; unset flags default to #f.

procedure

(dtc-status->byte s) → (integer-in 0 255)

  s : dtc-status?
Packs the flags into the wire byte.

procedure

(byte->dtc-status b) → dtc-status?

  b : (integer-in 0 255)
Unpacks the wire byte into flags.

value

+dtc-group-all+ : (integer-in 0 16777215)

The 0xFFFFFF “all DTCs” group for clear-diagnostic-information.

5 Requests🔗ℹ

struct

(struct request (sid sub-function suppress? data))

  sid : (integer-in 0 255)
  sub-function : (or/c #f (integer-in 0 127))
  suppress? : boolean?
  data : bytes?
A parsed request: request-parse decodes any payload, resolving the sub-function/suppress-bit split for sub-function services.

procedure

(request-parse payload) → request?

  payload : bytes?
Decodes any request payload, resolving the sub-function/suppress-bit split for sub-function services.

procedure

(make-request sid    
  [#:sub-function sub    
  #:suppress? suppress?    
  #:data data]) → bytes?
  sid : (integer-in 0 255)
  sub : (or/c #f (integer-in 0 127)) = #f
  suppress? : boolean? = #f
  data : bytes? = #""
Generic request framing; raises when the service does not use sub-functions but one (or the suppress bit) is requested. The typed builders below are preferred.

procedure

(diagnostic-session-control type    
  [#:suppress? suppress?]) → bytes?
  type : (or/c 'default 'programming 'extended (integer-in 0 127))
  suppress? : boolean? = #f

procedure

(ecu-reset type [#:suppress? suppress?]) → bytes?

  type : (or/c 'hard 'key-off-on 'soft (integer-in 0 127))
  suppress? : boolean? = #f

procedure

(tester-present [#:suppress? suppress?]) → bytes?

  suppress? : boolean? = #f

procedure

(control-dtc-setting mode    
  [#:suppress? suppress?    
  #:data data]) → bytes?
  mode : (or/c 'on 'off (integer-in 0 127))
  suppress? : boolean? = #f
  data : bytes? = #""

procedure

(clear-diagnostic-information group) → bytes?

  group : (integer-in 0 16777215)
Typed builders for the session-management services.

procedure

(read-dtc-by-status-mask mask) → bytes?

  mask : (integer-in 0 255)

procedure

(report-number-of-dtc-by-status-mask mask) → bytes?

  mask : (integer-in 0 255)

procedure

(report-supported-dtc [#:suppress? suppress?]) → bytes?

  suppress? : boolean? = #f
ReadDTCInformation builders for the three most used sub-functions.

procedure

(security-access-request-seed level) → bytes?

  level : (integer-in 0 63)

procedure

(security-access-send-key level key) → bytes?

  level : (integer-in 0 63)
  key : bytes?
SecurityAccess framing. Levels pair up: odd levels request a seed, the following even level sends the key; the builders enforce the parity.

procedure

(read-data-by-identifier dids) → bytes?

  dids : (listof (integer-in 0 65535))

procedure

(write-data-by-identifier did data) → bytes?

  did : (integer-in 0 65535)
  data : bytes?

procedure

(input-output-control-by-identifier did    
  data) → bytes?
  did : (integer-in 0 65535)
  data : bytes?
Data-identifier services; DIDs are 16-bit big-endian.

procedure

(routine-control action    
  routine-id    
  [#:record record    
  #:suppress? suppress?]) → bytes?
  action : (or/c 'start 'stop 'request-results (integer-in 0 127))
  routine-id : (integer-in 0 65535)
  record : bytes? = #""
  suppress? : boolean? = #f

procedure

(read-memory-by-address address    
  address-bytes    
  size    
  size-bytes) → bytes?
  address : integer?
  address-bytes : (integer-in 1 15)
  size : integer?
  size-bytes : (integer-in 1 15)

procedure

(write-memory-by-address address    
  address-bytes    
  data) → bytes?
  address : integer?
  address-bytes : (integer-in 1 15)
  data : bytes?

procedure

(request-download data-format-id    
  address    
  address-bytes    
  size    
  size-bytes) → bytes?
  data-format-id : (integer-in 0 255)
  address : integer?
  address-bytes : (integer-in 1 15)
  size : integer?
  size-bytes : (integer-in 1 15)

procedure

(transfer-data counter [#:data data]) → bytes?

  counter : (integer-in 0 255)
  data : bytes? = #""

procedure

(request-transfer-exit [#:data data]) → bytes?

  data : bytes? = #""
Memory and transfer services. The addressAndLengthFormatIdentifier (high nibble: address byte count, low nibble: size byte count) is built by encode-address-and-length and read back by decode-address-and-length.

procedure

(encode-address-and-length address    
  address-bytes    
  size    
  size-bytes) → bytes?
  address : integer?
  address-bytes : (integer-in 1 15)
  size : integer?
  size-bytes : (integer-in 1 15)
Builds the addressAndLengthFormatIdentifier followed by the big-endian address and size.

procedure

(decode-address-and-length payload) → 
integer? integer?
  payload : bytes?
Inverse of encode-address-and-length: returns the address and the size.

6 Responses🔗ℹ

procedure

(response-parse payload)

 → (or/c positive-response? negative-response?)
  payload : bytes?
Classifies one response payload: bytes starting with 7F become a negative-response; everything else must be a positive response (SID | 0x40) and is split into echoed sub-function (for sub-function services) and the response record.

struct

(struct positive-response (sid sub-function data))

  sid : (integer-in 0 255)
  sub-function : (or/c #f (integer-in 0 127))
  data : bytes?
A parsed positive response; sid is the request service ID.

Record decoders for the common positive responses. Each takes the positive-response-data byte string and returns the decoded fields; length mismatches raise.

DiagnosticSessionControl: session type, P2 in milliseconds, P2* in milliseconds (the wire value counts units of 10 ms).

procedure

(security-seed-response data) → bytes?

  data : bytes?
SecurityAccess requestSeed: the seed bytes.

RequestDownload/RequestUpload: maxNumberOfBlockLength.

procedure

(number-of-dtc-response data) → 
(integer-in 0 255)
exact-nonnegative-integer?
  data : bytes?
ReadDTCInformation reportNumberOfDTCByStatusMask: DTC format identifier and the 24-bit DTC count.

procedure

(dtc-list-response data) → (listof dtc?)

  data : bytes?
ReadDTCInformation reportDTCByStatusMask / reportSupportedDTC: the DTC records as dtc? values.

procedure

(transfer-data-response data) → 
(integer-in 0 255) bytes?
  data : bytes?
TransferData: the echoed block sequence counter and the response record.

7 Caveats🔗ℹ

  • Authentication (SID 0x29) framing is included in the service table but no typed builder yet; use make-request.

  • Security algorithms (seed/key) are ECU-specific by design and stay with the caller.

  • The library does not manage sessions, timing (P2/P2*) or retransmission; pair it with isotp for the transport layer.