1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
#ifndef __OBD2_H__
#define __OBD2_H__
#include <isotp/isotp.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
// TODO This isn't true for multi frame messages - we may need to dynamically
// allocate this in the future
#define MAX_OBD2_PAYLOAD_LENGTH 7
#define VIN_LENGTH 17
typedef enum {
DIAGNOSTIC_REQUEST_TYPE_PID,
DIAGNOSTIC_REQUEST_TYPE_DTC,
DIAGNOSTIC_REQUEST_TYPE_MIL_STATUS,
DIAGNOSTIC_REQUEST_TYPE_VIN
} DiagnosticRequestType;
typedef struct {
DiagnosticRequestType type;
uint16_t arbitration_id;
uint8_t mode;
uint16_t pid;
uint8_t pid_length;
uint8_t payload[MAX_OBD2_PAYLOAD_LENGTH];
uint8_t payload_length;
} DiagnosticRequest;
// TODO I don't like this, it's hard coding isotp library stuff here
typedef bool (*SendIsoTpMessageShim)(IsoTpHandle* handle,
const uint8_t* payload, uint16_t payload_size);
// 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_SUCCESS = 0x0,
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;
bool completed;
uint16_t pid;
DiagnosticNegativeResponseCode negative_response_code;
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 struct {
DiagnosticRequest request;
bool success;
bool completed;
IsoTpShims isotp_shims;
IsoTpHandle isotp_send_handle;
IsoTpHandle isotp_receive_handle;
DiagnosticResponseReceived callback;
DiagnosticMilStatusReceived mil_status_callback;
DiagnosticVinReceived vin_callback;
} DiagnosticRequestHandle;
typedef enum {
DIAGNOSTIC_STANDARD_PID,
DIAGNOSTIC_ENHANCED_PID
} DiagnosticPidRequestType;
typedef struct {
SetTimerShim set_timer;
SendCanMessageShim send_can_message;
LogShim log;
} 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);
DiagnosticResponse diagnostic_receive_can_frame(DiagnosticShims* shims,
DiagnosticRequestHandle* handle,
const uint16_t arbitration_id, const uint8_t data[],
const uint8_t size);
#ifdef __cplusplus
}
#endif
#endif // __OBD2_H__
|