X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=README.mkd;h=aa0d7fd7cac21d6d23e20f3f5aa796f2f65ca82e;hb=ca20db3dd978871bbb9f01f3c862b510c03d1dc4;hp=28aee7213b9b32f5c406742c6860da9827246994;hpb=7a5e3a7037170aacc3f3438f1267c635358d91a8;p=apps%2Fagl-service-can-low-level.git diff --git a/README.mkd b/README.mkd index 28aee721..aa0d7fd7 100644 --- a/README.mkd +++ b/README.mkd @@ -1,79 +1,189 @@ -OBD-II Support Library in C -============================= +Unified Diagnostic Services (UDS) Support Library in C +====================================================== + +This is a platform agnostic C library that implements the Unified Diagnostics +Services protocol for automotive electronics. UDS is documented in ISO 14229 and +is the underpinning for the more well-known On-board Diagnostics (OBD) standard. +The library currently supports UDS running over CAN (ISO 15765-4), which uses +the ISO-TP (ISO 15765-2) protocol for message framing. + +This library doesn't assume anything about the source of your diagnostic message +requests or underlying interface to the CAN bus. It uses dependency injection to +give you complete control. + +## Usage + +First, create some shim functions to let this library use your lower level +system: + + // required, this must send a single CAN message with the given arbitration + // ID (i.e. the CAN message ID) and data. The size will never be more than 8 + // bytes. + bool send_can(const uint32_t arbitration_id, const uint8_t* data, + const uint8_t size) { + ... + } -TODO isotp needs to accept responses on an ID other that the request's arb id - -or maybe we have 2 isotp instances, for rx and tx. + // optional, provide to receive debugging log messages + void debug(const char* format, ...) { + ... + } -* Response's arb id is assigned ID plus 0x8 -* Functional daignostic request (Broadcast), 0x7df arb id -* Standard ECU requests to 0x7e0 - 0x7e7 -* Some requests don't need PIDs - e.g. mode 3 - * some responses don't have PIDs, just payload - e.g. mode 3 + // not used in the current version + void set_timer(uint16_t time_ms, void (*callback)) { + ... + } -* Service ID + 0x40 == positive response -* Response includes PID echo - use this to match up request/response -* Response ID, ISO-TP PCI, ISO-TP length, service response ID, PID echo, data +With your shims in place, create a `DiagnosticShims` object to pass them around: -I send the request and give a callback -when the response arrives for the matchin service and PID - on any arb id, since -this may be a broadcast - call my callback. + DiagnosticShims shims = diagnostic_init_shims(debug, send_can, set_timer); +With your shims in hand, send a simple PID request to the standard broadcast +address, `0x7df` (we use the constant `OBD2_FUNCTIONAL_BROADCAST_ID` here): -TODO do you want to provide the callback to the Handler, or to each -individual send?o -ok, what are the use cases here. + // Optional: This is your callback that will be called the response to your + // diagnostic request is received. + void response_received_handler(const DiagnosticResponse* response) { + // You received a response! Do something with it. + } -you're going to request a few PIDs over and over again at some frequency -you're going to request DTCs once and read the response -you're going to clear DTCs once + DiagnosticRequestHandle handle = diagnostic_request_pid(&shims, + DIAGNOSTIC_STANDARD_PID, // this is a standard PID request, not an extended or enhanced one + OBD2_FUNCTIONAL_BROADCAST_ID, // the request is going out to the broadcast arbitration ID + 0x2, // we want PID 0x2 + response_received_handler); // our callback (optional, use NULL if you don't have one) + + if(handle.completed) { + if(!handle.success) { + // something happened and it already failed - possibly we aren't + // able to send CAN messages + return; + } else { + // this should never occur right away - you need to receive a fresh + // CAN message first + } + } else { + while(true) { + // Continue to read from CAN, passing off each message to the handle. + // This will return a 'completed' DiagnosticResponse when the when + // the request is completely sent and the response is received + // (which may take more than 1 CAN frames) + DiagnosticResponse response = diagnostic_receive_can_frame(&shims, + &handle, can_message_id, can_data, sizeof(can_data)); + + if(response.completed && handle.completed) { + if(handle.success) { + if(response.success) { + // The request was sent successfully, the response was + // received successfully, and it was a positive response - we + // got back some data! + } else { + // The request was sent successfully, the response was + // received successfully, BUT it was a negative response + // from the other node. + printf("This is the error code: %d", response.negative_response_code); + } + } else { + // Some other fatal error ocurred - we weren't able to send + // the request or receive the response. The CAN connection + // may be down. + } + } + } + } -i'd rather use the same diagnostic handler to send all of the differet messages -rather than create one for each use. that way ai can +### Requests for other modes -but that does complicate the memory management because it'll need to create -some internal data structures. +If you want to do more besides PID requests on mode 0x1 and 0x22, there's a +lower level API you can use. Here's how to make a mode 3 request to get DTCs. -at the simplest, what does this handler have to do? + DiagnosticRequest request = { + arbitration_id: OBD2_FUNCTIONAL_BROADCAST_ID, + mode: OBD2_MODE_EMISSIONS_DTC_REQUEST + }; + DiagnosticRequestHandle handle = diagnostic_request(&SHIMS, &request, NULL); + + if(handle.completed) { + if(!handle.success) { + // something happened and it already failed - possibly we aren't + // able to send CAN messages + return; + } else { + // this should never occur right away - you need to receive a fresh + // CAN message first + } + } else { + while(true) { + // Continue to read from CAN, passing off each message to the handle. + // This will return a 'completed' DiagnosticResponse when the when + // the request is completely sent and the response is received + // (which may take more than 1 CAN frames) + DiagnosticResponse response = diagnostic_receive_can_frame(&shims, + &handle, can_message_id, can_data, sizeof(can_data)); + + if(response.completed && handle.completed) { + if(handle.success) { + if(response.success) { + // The request was sent successfully, the response was + // received successfully, and it was a positive response - we + // got back some data! + printf("The DTCs are: "); + for(int i = 0; i < response.payload_length; i++) { + printf("0x%x ", response.payload[i]); + } + } else { + // The request was sent successfully, the response was + // received successfully, BUT it was a negative response + // from the other node. + printf("This is the error code: %d", response.negative_response_code); + } + } else { + // Some other fatal error ocurred - we weren't able to send + // the request or receive the response. The CAN connection + // may be down. + } + } + } + } -* store the request arb id, mode, pid, and payload locally -* send a can message -* get all new can messages passed to it -* Check the incoming can message to see if it matches one of the standard ECU - response IDs, or our arb ID + 0x8 -* if it matches, parse the diagnostic response and call the callback +## Dependencies -that seems pretty simple and not worth greatly increasing the internal state to -handle multiple requests +This library requires 2 dependencies: -what we need is another layer on top of that to handle the repeated requests. +* [isotp-c](https://github.com/openxc/isotp-c) +* [bitfield-c](https://github.com/openxc/bitfield-c) -## API +## Testing - void my_callback(const DiagnosticResponse* response) { - } +The library includes a test suite that uses the `check` C unit test library. - DiagnosticRequestHandle handle = diagnostic_init(my_callback); - DiagnosticRequest request = { - arbitratin_id: 0x7df, - mode: OBD2_MODE_POWERTRAIN_DIAGNOSTIC_REQUEST, - pid: 3 - }; + $ make test - diagnostic_send(&handle, &request); - while(true) { - diagnostic_handle_can_frame(&handle, 42, data, 8); - } +You can also see the test coverage if you have `lcov` installed and the +`BROWSER` environment variable set to your choice of web browsers: - diagnostic_request_pid(&handle, DIAGNOSTIC_STANDARD_PID, 42 + $ BROWSER=google-chrome-stable make coverage - diagnostic_destroy(&handle); +## OBD-II Basics -## Testing +TODO diagram out a request, response and error response -The library includes a test suite that uses the `check` C unit test library. +* store the request arb id, mode, pid, and payload locally +* send a can message +* get all new can messages passed to it +* Check the incoming can message to see if it matches one of the standard ECU + response IDs, or our arb ID + 0x8 +* if it matches, parse the diagnostic response and call the callback - $ make test + +## Future Notes + +you're going to request a few PIDs over and over again at some frequency +you're going to request DTCs once and read the response +you're going to clear DTCs once + +we need another layer on top of that to handle the repeated requests. ## Authors