diff options
-rw-r--r-- | README.mkd | 66 | ||||
m--------- | deps/isotp-c | 10 | ||||
-rw-r--r-- | src/obd2/obd2.c | 47 | ||||
-rw-r--r-- | src/obd2/obd2.h | 173 | ||||
-rw-r--r-- | tests/common.c | 0 | ||||
-rw-r--r-- | tests/test_core.c (renamed from tests/tests.c) | 3 |
6 files changed, 292 insertions, 7 deletions
@@ -1,8 +1,74 @@ OBD-II Support Library in C ============================= +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. + +* 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 + +* 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 + +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. + + +TODO do you want to provide the callback to the Handler, or to each +individual send?o +ok, what are the use cases here. + +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 + +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 + +but that does complicate the memory management because it'll need to create +some internal data structures. + +at the simplest, what does this handler have to do? + +* 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 + +that seems pretty simple and not worth greatly increasing the internal state to +handle multiple requests + +what we need is another layer on top of that to handle the repeated requests. + ## API + void my_callback(const DiagnosticResponse* response) { + } + + DiagnosticRequestHandler handler = diagnostic_init(my_callback); + DiagnosticRequest request = { + arbitratin_id: 0x7df, + mode: OBD2_MODE_POWERTRAIN_DIAGNOSTIC_REQUEST, + pid: 3 + }; + + diagnostic_send(&handler, &request); + while(true) { + diagnostic_handle_can_frame(&handler, 42, data, 8); + } + + diagnostic_request_pid(&handler, DIAGNOSTIC_STANDARD_PID, 42 + + diagnostic_destroy(&handler); + ## Testing The library includes a test suite that uses the `check` C unit test library. diff --git a/deps/isotp-c b/deps/isotp-c -Subproject 368d36cc15b69882e9d21803f3b8aa7a1c97736 +Subproject bc7c0205d8dd7550060f6f0bbe8e2064d28ace8 diff --git a/src/obd2/obd2.c b/src/obd2/obd2.c new file mode 100644 index 00000000..9b4ea92d --- /dev/null +++ b/src/obd2/obd2.c @@ -0,0 +1,47 @@ +#include <obd2/obd2.h> + +DiagnosticShims diagnostic_init_shims(LogShim log, + SendCanMessageShim send_can_message, + SetTimerShim set_timer) { +} + +DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims, + DiagnosticRequest* request, DiagnosticResponseReceived callback) { +} + +// decide mode 0x1 / 0x22 based on pid type +DiagnosticRequestHandle diagnostic_request_pid(DiagnosticShims* shims, + DiagnosticPidRequestType pid_request_type, uint16_t pid, + DiagnosticResponseReceived callback) { +} + +// TODO request malfunction indicator light (MIL) status - request mode 1 pid 1, +// parse first bit +DiagnosticRequestHandle diagnostic_request_malfunction_indicator_status( + DiagnosticShims* shims, + DiagnosticMilStatusReceived callback) { +} + +DiagnosticRequestHandle diagnostic_request_vin(DiagnosticShims* shims, + DiagnosticVinReceived callback) { +} + +DiagnosticRequestHandle diagnostic_request_dtc(DiagnosticShims* shims, + DiagnosticTroubleCodeType dtc_type, + DiagnosticTroubleCodesReceived callback) { +} + +bool diagnostic_clear_dtc(DiagnosticShims* shims) { +} + +// before calling the callback, split up the received bytes into 1 or 2 byte +// chunks depending on the mode so the final pid list is actual 1 or 2 byte PIDs +// TODO request supported PIDs - request PID 0 and parse 4 bytes in response +DiagnosticRequestHandle diagnostic_enumerate_pids(DiagnosticShims* shims, + DiagnosticRequest* request, DiagnosticPidEnumerationReceived callback) { +} + +void diagnostic_receive_can_frame(DiagnosticRequestHandle* handler, + const uint16_t arbitration_id, const uint8_t data[], + const uint8_t size) { +} diff --git a/src/obd2/obd2.h b/src/obd2/obd2.h new file mode 100644 index 00000000..e6b5bc96 --- /dev/null +++ b/src/obd2/obd2.h @@ -0,0 +1,173 @@ +#ifndef __OBD2_H__ +#define __OBD2_H__ + +#include <isotp/isotp.h> +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_OBD2_PAYLOAD_LENGTH 7 +#define VIN_LENGTH 17 + +typedef void (*LogShim)(const char* message); +typedef bool (*SendCanMessageShim)(const uint16_t arbitration_id, + const uint8_t* data, const uint8_t size); +typedef bool (*SetTimerShim)(uint16_t time_ms, void (*callback)); + +typedef struct { + uint16_t arbitration_id; + uint8_t mode; + uint16_t pid; + uint8_t payload[MAX_OBD2_PAYLOAD_LENGTH]; + uint8_t payload_length; +} DiagnosticRequest; + +// Thanks to +// http://www.canbushack.com/blog/index.php?title=scanning-for-diagnostic-data&more=1&c=1&tb=1&pb=1 +// for the list of NRCs +typedef enum { + NRC_SERVICE_NOT_SUPPORTED = 0x11, + NRC_SUB_FUNCTION_NOT_SUPPORTED = 0x12, + NRC_CONDITIONS_NOT_CORRECT = 0x22, + NRC_REQUEST_OUT_OF_RANGE = 0x31, + NRC_SECURITY_ACCESS_DENIED = 0x33, + NRC_INVALID_KEY = 0x35, + NRC_TOO_MANY_ATTEMPS = 0x36, + NRC_TIME_DELAY_NOT_EXPIRED = 0x37, + NRC_RESPONSE_PENDING = 0x78 +} DiagnosticNegativeResponseCode; + +typedef enum { + OBD2_MODE_POWERTRAIN_DIAGNOSTIC_REQUEST = 0x1, + OBD2_MODE_POWERTRAIN_FREEZE_FRAME_REQUEST = 0x2, + OBD2_MODE_EMISSIONS_DTC_REQUEST = 0x3, + OBD2_MODE_EMISSIONS_DTC_CLEAR = 0x4, + // 0x5 is for non-CAN only + // OBD2_MODE_OXYGEN_SENSOR_TEST = 0x5, + OBD2_MODE_TEST_RESULTS = 0x6, + OBD2_MODE_DRIVE_CYCLE_DTC_REQUEST = 0x7, + OBD2_MODE_CONTROL = 0x8, + OBD2_MODE_VEHICLE_INFORMATION = 0x9, + OBD2_MODE_PERMANENT_DTC_REQUEST = 0xa, + // this one isn't technically in OBD2, but both of the enhanced standards + // have their PID requests at 0x22 + OBD2_MODE_ENHANCED_DIAGNOSTIC_REQUEST = 0x22 +} DiagnosticMode; + +typedef enum { + DTC_EMISSIONS, + DTC_DRIVE_CYCLE, + DTC_PERMANENT +} DiagnosticTroubleCodeType; + +typedef struct { + uint16_t arbitration_id; + uint8_t mode; + bool success; + // if mode is one with a PID, read the correct numbers of PID bytes (1 or 2) + // into this field, then store the remainder of the payload in the payload + // field + uint16_t pid; + DiagnosticNegativeResponseCode negative_response_code; + // if response mode is a negative response, read first byte of payload into + // NRC and store remainder of payload in payload field + uint8_t payload[MAX_OBD2_PAYLOAD_LENGTH]; + uint8_t payload_length; +} DiagnosticResponse; + +typedef enum { + POWERTRAIN = 0x0, + CHASSIS = 0x1, + BODY = 0x2, + NETWORK = 0x3 +} DiagnosticTroubleCodeGroup; + +typedef struct { + DiagnosticTroubleCodeGroup group; + uint8_t group_num; + uint8_t code; +} DiagnosticTroubleCode; + +typedef void (*DiagnosticResponseReceived)(const DiagnosticResponse* response); +typedef void (*DiagnosticMilStatusReceived)(bool malfunction_indicator_status); +typedef void (*DiagnosticVinReceived)(uint8_t vin[]); +typedef void (*DiagnosticTroubleCodesReceived)( + DiagnosticMode mode, DiagnosticTroubleCode* codes); +typedef void (*DiagnosticPidEnumerationReceived)( + const DiagnosticResponse* response, uint16_t* pids); + +// TODO should we enumerate every OBD-II PID? need conversion formulas, too +typedef struct { + uint16_t pid; + uint8_t bytes_returned; + float min_value; + float max_value; +} DiagnosticParameter; + +typedef enum { + DIAGNOSTIC_REQUEST_TYPE_PID, + DIAGNOSTIC_REQUEST_TYPE_DTC, + DIAGNOSTIC_REQUEST_TYPE_MIL_STATUS, + DIAGNOSTIC_REQUEST_TYPE_VIN +} DiagnosticRequestType; + +typedef struct { + IsoTpHandler isotp_handler; + DiagnosticRequestType type; + DiagnosticResponseReceived callback; + DiagnosticMilStatusReceived mil_status_callback; + DiagnosticVinReceived vin_callback; + bool status; +} DiagnosticRequestHandle; + +typedef enum { + DIAGNOSTIC_STANDARD_PID, + DIAGNOSTIC_ENHANCED_PID +} DiagnosticPidRequestType; + +typedef struct { + IsoTpShims isotp_shims; + LogShim log; + SendCanMessageShim send_can_message; +} DiagnosticShims; + +DiagnosticShims diagnostic_init_shims(LogShim log, + SendCanMessageShim send_can_message, + SetTimerShim set_timer); + +DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims, + DiagnosticRequest* request, DiagnosticResponseReceived callback); + +// decide mode 0x1 / 0x22 based on pid type +DiagnosticRequestHandle diagnostic_request_pid(DiagnosticShims* shims, + DiagnosticPidRequestType pid_request_type, uint16_t pid, + DiagnosticResponseReceived callback); + +DiagnosticRequestHandle diagnostic_request_malfunction_indicator_status( + DiagnosticShims* shims, + DiagnosticMilStatusReceived callback); + +DiagnosticRequestHandle diagnostic_request_vin(DiagnosticShims* shims, + DiagnosticVinReceived callback); + +DiagnosticRequestHandle diagnostic_request_dtc(DiagnosticShims* shims, + DiagnosticTroubleCodeType dtc_type, + DiagnosticTroubleCodesReceived callback); + +bool diagnostic_clear_dtc(DiagnosticShims* shims); + +DiagnosticRequestHandle diagnostic_enumerate_pids(DiagnosticShims* shims, + DiagnosticRequest* request, DiagnosticPidEnumerationReceived callback); + +void diagnostic_receive_can_frame(DiagnosticRequestHandle* handler, + const uint16_t arbitration_id, const uint8_t data[], + const uint8_t size); + +#ifdef __cplusplus +} +#endif + +#endif // __OBD2_H__ diff --git a/tests/common.c b/tests/common.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/common.c diff --git a/tests/tests.c b/tests/test_core.c index 68745652..65af8580 100644 --- a/tests/tests.c +++ b/tests/test_core.c @@ -1,8 +1,7 @@ #include <check.h> #include <stdint.h> #include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> +#include <stdbool.h> void setup() { |