rusEFI
The most advanced open source ECU
obd2.cpp
Go to the documentation of this file.
1 /*
2  * @file obd2.cpp
3  *
4  * ISO 15765-4
5  * http://en.wikipedia.org/wiki/OBD-II_PIDs
6  *
7  * @date Jun 9, 2015
8  * @author Andrey Belomutskiy, (c) 2012-2020
9  *
10  * This file is part of rusEfi - see http://rusefi.com
11  *
12  * rusEfi is free software; you can redistribute it and/or modify it under the terms of
13  * the GNU General Public License as published by the Free Software Foundation; either
14  * version 3 of the License, or (at your option) any later version.
15  *
16  * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
17  * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along with this program.
21  * If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "pch.h"
25 
26 #if EFI_CAN_SUPPORT
27 
28 #include "obd2.h"
29 #include "can.h"
30 #include "can_msg_tx.h"
31 #include "fuel_math.h"
32 
33 static const int16_t supportedPids0120[] = {
34  PID_MONITOR_STATUS,
35  PID_FUEL_SYSTEM_STATUS,
36  PID_ENGINE_LOAD,
37  PID_COOLANT_TEMP,
38  PID_STFT_BANK1,
39  PID_STFT_BANK2,
40  PID_INTAKE_MAP,
41  PID_RPM,
42  PID_SPEED,
43  PID_TIMING_ADVANCE,
44  PID_INTAKE_TEMP,
45  PID_THROTTLE,
46  -1
47 };
48 
49 static const int16_t supportedPids2140[] = {
50  PID_FUEL_AIR_RATIO_1,
51  -1
52 };
53 
54 static const int16_t supportedPids4160[] = {
55  PID_FUEL_RATE,
56  -1
57 };
58 
59 static void obdSendPacket(int mode, int PID, int numBytes, uint32_t iValue, size_t busIndex) {
60  CanTxMessage resp(CanCategory::OBD, OBD_TEST_RESPONSE);
61 
62  // Respond on the same bus we got the request from
63  resp.busIndex = busIndex;
64 
65  // write number of bytes
66  resp[0] = (uint8_t)(2 + numBytes);
67  // write 2 bytes of header
68  resp[1] = (uint8_t)(0x40 + mode);
69  resp[2] = (uint8_t)PID;
70  // write N data bytes
71  for (int i = 8 * (numBytes - 1), j = 3; i >= 0; i -= 8, j++) {
72  resp[j] = (uint8_t)((iValue >> i) & 0xff);
73  }
74 }
75 
76 #define _1_MODE 1
77 
78 static void obdSendValue(int mode, int PID, int numBytes, float value, size_t busIndex) {
79  efiAssertVoid(ObdCode::CUSTOM_ERR_6662, numBytes <= 2, "invalid numBytes");
80  int iValue = (int)efiRound(value, 1.0f);
81  // clamp to uint8_t (0..255) or uint16_t (0..65535)
82  iValue = maxI(minI(iValue, (numBytes == 1) ? 255 : 65535), 0);
83  obdSendPacket(mode, PID, numBytes, iValue, busIndex);
84 }
85 
86 
87 //#define MOCK_SUPPORTED_PIDS 0xffffffff
88 
89 static void obdWriteSupportedPids(int PID, int bitOffset, const int16_t *supportedPids, size_t busIndex) {
90  uint32_t value = 0;
91  // gather all 32 bit fields
92  for (int i = 0; i < 32 && supportedPids[i] > 0; i++)
93  value |= 1 << (31 + bitOffset - supportedPids[i]);
94 
95 #ifdef MOCK_SUPPORTED_PIDS
96  // for OBD debug
97  value = MOCK_SUPPORTED_PIDS;
98 #endif
99 
100  obdSendPacket(1, PID, 4, value, busIndex);
101 }
102 
103 static void handleGetDataRequest(const CANRxFrame& rx, size_t busIndex) {
104  int pid = rx.data8[2];
105  switch (pid) {
106  case PID_SUPPORTED_PIDS_REQUEST_01_20:
107  obdWriteSupportedPids(pid, 1, supportedPids0120, busIndex);
108  break;
109  case PID_SUPPORTED_PIDS_REQUEST_21_40:
110  obdWriteSupportedPids(pid, 21, supportedPids2140, busIndex);
111  break;
112  case PID_SUPPORTED_PIDS_REQUEST_41_60:
113  obdWriteSupportedPids(pid, 41, supportedPids4160, busIndex);
114  break;
115  case PID_MONITOR_STATUS:
116  obdSendPacket(1, pid, 4, 0, busIndex); // todo: add statuses
117  break;
118  case PID_FUEL_SYSTEM_STATUS:
119  // todo: add statuses
120  obdSendValue(_1_MODE, pid, 2, (2<<8)|(0), busIndex); // 2 = "Closed loop, using oxygen sensor feedback to determine fuel mix"
121  break;
122  case PID_ENGINE_LOAD:
123  obdSendValue(_1_MODE, pid, 1, getFuelingLoad() * ODB_TPS_BYTE_PERCENT, busIndex);
124  break;
125  case PID_COOLANT_TEMP:
126  obdSendValue(_1_MODE, pid, 1, Sensor::getOrZero(SensorType::Clt) + ODB_TEMP_EXTRA, busIndex);
127  break;
128  case PID_STFT_BANK1:
129  obdSendValue(_1_MODE, pid, 1, 128 * engine->stftCorrection[0], busIndex);
130  break;
131  case PID_STFT_BANK2:
132  obdSendValue(_1_MODE, pid, 1, 128 * engine->stftCorrection[1], busIndex);
133  break;
134  case PID_INTAKE_MAP:
135  obdSendValue(_1_MODE, pid, 1, Sensor::getOrZero(SensorType::Map), busIndex);
136  break;
137  case PID_RPM:
138  obdSendValue(_1_MODE, pid, 2, Sensor::getOrZero(SensorType::Rpm) * ODB_RPM_MULT, busIndex); // rotation/min. (A*256+B)/4
139  break;
140  case PID_SPEED:
141  obdSendValue(_1_MODE, pid, 1, Sensor::getOrZero(SensorType::VehicleSpeed), busIndex);
142  break;
143  case PID_TIMING_ADVANCE: {
144  float timing = engine->engineState.timingAdvance[0];
145  timing = (timing > 360.0f) ? (timing - 720.0f) : timing;
146  obdSendValue(_1_MODE, pid, 1, (timing + 64.0f) * 2.0f, busIndex); // angle before TDC. (A/2)-64
147  break;
148  }
149  case PID_INTAKE_TEMP:
150  obdSendValue(_1_MODE, pid, 1, Sensor::getOrZero(SensorType::Iat) + ODB_TEMP_EXTRA, busIndex);
151  break;
152  case PID_INTAKE_MAF:
153  obdSendValue(_1_MODE, pid, 2, Sensor::getOrZero(SensorType::Maf) * 100.0f, busIndex); // grams/sec (A*256+B)/100
154  break;
155  case PID_THROTTLE:
156  obdSendValue(_1_MODE, pid, 1, Sensor::getOrZero(SensorType::Tps1) * ODB_TPS_BYTE_PERCENT, busIndex); // (A*100/255)
157  break;
158  case PID_FUEL_AIR_RATIO_1: {
159  float lambda = clampF(0, Sensor::getOrZero(SensorType::Lambda1), 1.99f);
160 
161  uint16_t scaled = lambda * 32768;
162 
163  obdSendPacket(1, pid, 4, scaled << 16, busIndex);
164  break;
165  } case PID_FUEL_RATE: {
166  float gPerSecond = engine->module<TripOdometer>()->getConsumptionGramPerSecond();
167  float gPerHour = gPerSecond * 3600;
168  float literPerHour = gPerHour * 0.00139f;
169  obdSendValue(_1_MODE, pid, 2, literPerHour * 20.0f, busIndex); // L/h. (A*256+B)/20
170  break;
171  } default:
172  // ignore unhandled PIDs
173  break;
174  }
175 }
176 
177 static void handleDtcRequest(int numCodes, ObdCode* dtcCode) {
178  // TODO: this appears to be unfinished?
179  UNUSED(numCodes);
180  UNUSED(dtcCode);
181 
182  // int numBytes = numCodes * 2;
183  // // write CAN-TP Single Frame header?
184  // txmsg.data8[0] = (uint8_t)((0 << 4) | numBytes);
185  // for (int i = 0, j = 1; i < numCodes; i++) {
186  // txmsg.data8[j++] = (uint8_t)((dtcCode[i] >> 8) & 0xff);
187  // txmsg.data8[j++] = (uint8_t)(dtcCode[i] & 0xff);
188  // }
189 }
190 
191 #if HAL_USE_CAN
192 void obdOnCanPacketRx(const CANRxFrame& rx, size_t busIndex) {
193  if (CAN_SID(rx) != OBD_TEST_REQUEST) {
194  return;
195  }
196 
197  if (rx.data8[0] == _OBD_2 && rx.data8[1] == OBD_CURRENT_DATA) {
198  handleGetDataRequest(rx, busIndex);
199  } else if (rx.data8[0] == 1 && rx.data8[1] == OBD_STORED_DIAGNOSTIC_TROUBLE_CODES) {
200  // todo: implement stored/pending difference?
202  } else if (rx.data8[0] == 1 && rx.data8[1] == OBD_PENDING_DIAGNOSTIC_TROUBLE_CODES) {
203  // todo: implement stored/pending difference?
205  }
206 }
207 #endif /* HAL_USE_CAN */
208 
209 #endif /* EFI_CAN_SUPPORT */
size_t busIndex
Definition: can_msg_tx.h:48
float stftCorrection[STFT_BANK_COUNT]
Definition: engine.h:285
EngineState engineState
Definition: engine.h:310
constexpr auto & module()
Definition: engine.h:174
WarningCodeState warnings
Definition: engine_state.h:41
angle_t timingAdvance[MAX_CYLINDER_COUNT]
Definition: engine_state.h:58
static float getOrZero(SensorType type)
Definition: sensor.h:92
ObdCode lastErrorCode
Definition: engine_parts.h:63
float efiRound(float value, float precision)
Definition: efilib.cpp:33
Engine * engine
float getFuelingLoad()
Definition: engine_math.cpp:47
UNUSED(samplingTimeSeconds)
static void handleDtcRequest(int numCodes, ObdCode *dtcCode)
Definition: obd2.cpp:177
static void obdSendPacket(int mode, int PID, int numBytes, uint32_t iValue, size_t busIndex)
Definition: obd2.cpp:59
static const int16_t supportedPids4160[]
Definition: obd2.cpp:54
static void obdWriteSupportedPids(int PID, int bitOffset, const int16_t *supportedPids, size_t busIndex)
Definition: obd2.cpp:89
static void obdSendValue(int mode, int PID, int numBytes, float value, size_t busIndex)
Definition: obd2.cpp:78
static const int16_t supportedPids2140[]
Definition: obd2.cpp:49
static void handleGetDataRequest(const CANRxFrame &rx, size_t busIndex)
Definition: obd2.cpp:103
static const int16_t supportedPids0120[]
Definition: obd2.cpp:33
void obdOnCanPacketRx(const CANRxFrame &rx, size_t busIndex)
Definition: obd2.cpp:192
ObdCode
@ CUSTOM_ERR_6662
uint8_t data8[8]
Frame data.
Definition: can_mocks.h:55