3 #define MAX_DIAGNOSTIC_PAYLOAD_SIZE 6
4 #define MODE_BYTE_INDEX 0
5 #define PID_BYTE_INDEX 1
7 DiagnosticShims diagnostic_init_shims(LogShim log,
8 SendCanMessageShim send_can_message,
9 SetTimerShim set_timer) {
10 DiagnosticShims shims = {
11 send_can_message: send_can_message,
18 DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims,
19 DiagnosticRequest* request, DiagnosticResponseReceived callback) {
20 DiagnosticRequestHandle handle = {
21 type: DIAGNOSTIC_REQUEST_TYPE_PID,
26 uint8_t payload[MAX_DIAGNOSTIC_PAYLOAD_SIZE];
27 payload[MODE_BYTE_INDEX] = request->mode;
28 if(request->pid_length > 0) {
29 copy_bytes_right_aligned(request->pid, sizeof(request->pid),
30 PID_BYTE_INDEX, request->pid_length, payload, sizeof(payload));
32 if(request->payload_length > 0) {
33 memcpy(payload[PID_BYTE_INDEX + request->pid_length],
34 request->payload, request->payload_length);
37 handle.isotp_shims = isotp_init_shims(shims->log,
38 shims->send_can_message,
40 handle.isotp_send_handle = isotp_send(&handle.isotp_shims,
41 request->arbitration_id, payload,
42 1 + request->payload_length + request->pid_length,
43 // TODO is this ok to pass null here?
46 handle.isotp_receive_handle = isotp_receive(&handle.isotp_shims,
47 // TODO need to either always add 0x8 or let the user specify
48 request->arbitration_id + 0x8,
49 // TODO this callback is mostly useful for debugging stuff as it
50 // doesn't have the internal state we need to complete the
51 // diagnositc request - can we pass NULL or will that 'splode?
54 // when a can frame is received and passes to the diagnostic handle
55 // if we haven't successfuly sent the entire message yet, give it to the
57 // if we have sent it, give it to the isotp rx handle
58 // if we've received properly, mark this request as completed
59 // how do you get the result? we have it set up for callbacks but that's
60 // getting to be kind of awkward
62 // say it were to call a callback...what state would it need to pass?
64 // the iso-tp message received callback needs to pass the handle and the
67 // so in the obd2 library, we get a callback with an isotp message. how do
68 // we know what diag request i went with, and which diag callback to use? we
69 // could store context with the isotp handle. the problem is that context is
70 // self referential and on the stack, so we really can't get a pointer to
73 // the diagnostic response received callback needs to pass the diagnostic
74 // handle and the diag response
76 // let's say we simplify things and drop the callbacks.
78 // isotp_receive_can_frame returns an isotp handle with a complete message
79 // in it if one was received
81 // diagnostic_receive_can_frame returns a diaghandle if one was received
83 // so in the user code you would throw the can frame at each of your active
84 // diag handles and see if any return a completed message.
86 // is there any advantage to a callback approach? callbacks are useful when
87 // you have something that will block, bt we don't have anything that will
88 // block. it's async but we return immediately from each partial parsing
91 // argh, but will we need the callbacks when we add timers for isotp multi
94 // what are the timers for exactly?
96 // when sending multi frame, send 1 frame, wait for a response
97 // if it says send all, send all right away
98 // if it says flow control, set the time for the next send
99 // instead of creating a timer with an async callback, add a process_handle
100 // function that's called repeatedly in the main loop - if it's time to
101 // send, we do it. so there's a process_handle_send and receive_can_frame
102 // that are just called continuously from the main loop. it's a waste of a
103 // few cpu cycles but it may be more natural than callbacks.
105 // what woudl a timer callback look like...it would need to pass the handle
106 // and that's all. seems like a context void* would be able to capture all
107 // of the information but arg, memory allocation. look at how it's done in
108 // the other library again
113 DiagnosticRequestHandle diagnostic_request_pid(DiagnosticShims* shims,
114 DiagnosticPidRequestType pid_request_type, uint16_t pid,
115 DiagnosticResponseReceived callback) {
116 DiagnosticRequest request = {
117 mode: pid_request_type == DIAGNOSTIC_STANDARD_PID ? 0x1 : 0x22,
121 return diagnostic_request(shims, &request, callback);
124 DiagnosticResponse diagnostic_receive_can_frame(DiagnosticShims* shims,
125 DiagnosticRequestHandle* handle,
126 const uint16_t arbitration_id, const uint8_t data[],
127 const uint8_t size) {
128 if(!handle->isotp_send_handle.completed) {
129 } else if(!handle->isotp_receive_handle.completed) {
131 shims->log("Handle is already completed");
133 // TODO determine which isotp handler to pass it to based on our state
134 IsoTpMessage message = isotp_receive_can_frame(&handle->isotp_shims,
135 &handle->isotp_send_handle, arbitration_id, data, size);
137 DiagnosticResponse response = {
142 if(message.completed) {
146 // TODO everything below here is for future work...not critical for now.
148 DiagnosticRequestHandle diagnostic_request_malfunction_indicator_status(
149 DiagnosticShims* shims,
150 DiagnosticMilStatusReceived callback) {
151 // TODO request malfunction indicator light (MIL) status - request mode 1
152 // pid 1, parse first bit
155 DiagnosticRequestHandle diagnostic_request_vin(DiagnosticShims* shims,
156 DiagnosticVinReceived callback) {
159 DiagnosticRequestHandle diagnostic_request_dtc(DiagnosticShims* shims,
160 DiagnosticTroubleCodeType dtc_type,
161 DiagnosticTroubleCodesReceived callback) {
164 bool diagnostic_clear_dtc(DiagnosticShims* shims) {
167 DiagnosticRequestHandle diagnostic_enumerate_pids(DiagnosticShims* shims,
168 DiagnosticRequest* request, DiagnosticPidEnumerationReceived callback) {
169 // before calling the callback, split up the received bytes into 1 or 2 byte
170 // chunks depending on the mode so the final pid list is actual 1 or 2 byte PIDs
171 // TODO request supported PIDs - request PID 0 and parse 4 bytes in response