Add skeleton of the API and data structures.
authorChristopher Peplin <chris.peplin@rhubarbtech.com>
Mon, 30 Dec 2013 23:30:37 +0000 (18:30 -0500)
committerChristopher Peplin <chris.peplin@rhubarbtech.com>
Mon, 30 Dec 2013 23:30:37 +0000 (18:30 -0500)
README.mkd
deps/isotp-c
src/obd2/obd2.c [new file with mode: 0644]
src/obd2/obd2.h [new file with mode: 0644]
tests/common.c [new file with mode: 0644]
tests/test_core.c [moved from tests/tests.c with 94% similarity]

index 485f2cc..96b969e 100644 (file)
@@ -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.
index 368d36c..bc7c020 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 368d36cc15b69882e9d21803f3b8aa7a1c97736e
+Subproject commit bc7c0205d8dd7550060f6f0bbe8e2064d28ace8c
diff --git a/src/obd2/obd2.c b/src/obd2/obd2.c
new file mode 100644 (file)
index 0000000..9b4ea92
--- /dev/null
@@ -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 (file)
index 0000000..e6b5bc9
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
similarity index 94%
rename from tests/tests.c
rename to tests/test_core.c
index 6874565..65af858 100644 (file)
@@ -1,8 +1,7 @@
 #include <check.h>
 #include <stdint.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
+#include <stdbool.h>
 
 void setup() {