rusefi/firmware/controllers/sensors/sensor_checker.cpp

258 lines
8.6 KiB
C++

#include "pch.h"
// Decode what OBD code we should use for a particular [sensor, code] problem
static ObdCode getCode(SensorType type, UnexpectedCode code) {
switch (type) {
case SensorType::Tps1:
case SensorType::Tps1Primary:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_TPS1_Primary_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_TPS1_Primary_Low;
case UnexpectedCode::High: return ObdCode::OBD_TPS1_Primary_High;
case UnexpectedCode::Inconsistent: return ObdCode::OBD_TPS1_Correlation;
default: break;
} break;
case SensorType::Tps1Secondary:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_TPS1_Secondary_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_TPS1_Secondary_Low;
case UnexpectedCode::High: return ObdCode::OBD_TPS1_Secondary_High;
default: break;
} break;
case SensorType::Tps2:
case SensorType::Tps2Primary:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_TPS2_Primary_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_TPS2_Primary_Low;
case UnexpectedCode::High: return ObdCode::OBD_TPS2_Primary_High;
case UnexpectedCode::Inconsistent: return ObdCode::OBD_TPS2_Correlation;
default: break;
} break;
case SensorType::Tps2Secondary:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_TPS2_Secondary_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_TPS2_Secondary_Low;
case UnexpectedCode::High: return ObdCode::OBD_TPS2_Secondary_High;
default: break;
} break;
case SensorType::AcceleratorPedal:
case SensorType::AcceleratorPedalPrimary:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_PPS_Primary_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_PPS_Primary_Low;
case UnexpectedCode::High: return ObdCode::OBD_PPS_Primary_High;
case UnexpectedCode::Inconsistent: return ObdCode::OBD_PPS_Correlation;
default: break;
} break;
case SensorType::AcceleratorPedalSecondary:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_PPS_Secondary_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_PPS_Secondary_Low;
case UnexpectedCode::High: return ObdCode::OBD_PPS_Secondary_High;
default: break;
} break;
case SensorType::Map:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_Map_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_Map_Low;
case UnexpectedCode::High: return ObdCode::OBD_Map_High;
default: break;
} break;
case SensorType::Clt:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_Clt_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_Clt_Low;
case UnexpectedCode::High: return ObdCode::OBD_Clt_High;
default: break;
} break;
case SensorType::Iat:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_Iat_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_Iat_Low;
case UnexpectedCode::High: return ObdCode::OBD_Iat_High;
default: break;
} break;
case SensorType::FuelEthanolPercent:
switch (code) {
case UnexpectedCode::Timeout: return ObdCode::OBD_FlexSensor_Timeout;
case UnexpectedCode::Low: return ObdCode::OBD_FlexSensor_Low;
case UnexpectedCode::High: return ObdCode::OBD_FlexSensor_High;
default: break;
} break;
default:
break;
}
return ObdCode::None;
}
inline const char* describeUnexpected(UnexpectedCode code) {
switch (code) {
case UnexpectedCode::Timeout: return "has timed out";
case UnexpectedCode::High: return "input too high";
case UnexpectedCode::Low: return "input too low";
case UnexpectedCode::Inconsistent: return "is inconsistent";
case UnexpectedCode::Configuration: return "is misconfigured";
case UnexpectedCode::Unknown:
default:
return "unknown";
}
}
static void check(SensorType type) {
// Don't check sensors we don't have
if (!Sensor::hasSensor(type)) {
return;
}
auto result = Sensor::get(type);
// If the sensor is OK, nothing to check.
if (result) {
return;
}
ObdCode code = getCode(type, result.Code);
if (code != ObdCode::None) {
warning(code, "Sensor fault: %s %s", Sensor::getSensorName(type), describeUnexpected(result.Code));
}
}
#if BOARD_EXT_GPIOCHIPS > 0 && EFI_PROD_CODE
#if EFI_ENGINE_CONTROL
static ObdCode getCodeForInjector(int idx, brain_pin_diag_e diag) {
if (idx < 0 || idx >= MAX_CYLINDER_COUNT) {
return ObdCode::None;
}
if ((diag & PIN_OPEN) || (diag & PIN_SHORT_TO_GND)) {
return (ObdCode)((int)ObdCode::OBD_Injector_Circuit_1_Low + (idx * 3));
} else if ((diag & PIN_SHORT_TO_BAT) || (diag & PIN_OVERLOAD)) {
return (ObdCode)((int)ObdCode::OBD_Injector_Circuit_1_High + (idx * 3));
}
/* else common error code */
return (ObdCode)((int)ObdCode::OBD_Injector_Circuit_1 + idx);
}
#endif // EFI_ENGINE_CONTROL
static ObdCode getCodeForIgnition(int idx, brain_pin_diag_e diag) {
if (idx < 0 || idx >= MAX_CYLINDER_COUNT) {
return ObdCode::None;
}
// TODO: do something more intelligent with `diag`?
UNUSED(diag);
return (ObdCode)((int)ObdCode::OBD_Ignition_Circuit_1 + idx);
}
static uint8_t getTSErrorCode(brain_pin_diag_e diag)
{
/* Error codes reported to TS:
* 0 - output is not used
* 1 - ok status/no diagnostic available (TODO: separate codes)
* >1 - see brain_pin_diag_e, first least significant 1-bit position + 1 *
* Keep in sync with outputDiagErrorList in tunerstudio.template.ini
* Note:
* diag can be combination of few errors,
* while we report only one error to simplify hadling on TS side
* find position of least significant 1-bit */
return __builtin_ffs(diag) + 1;
}
#endif // BOARD_EXT_GPIOCHIPS > 0 && EFI_PROD_CODE
void SensorChecker::onSlowCallback() {
// Don't check when the ignition is off, or when it was just turned on (let things stabilize)
// TODO: also inhibit checking if we just did a flash burn, since that blocks the ECU for a few seconds.
bool shouldCheck = m_ignitionIsOn && m_timeSinceIgnOff.hasElapsedSec(5);
m_analogSensorsShouldWork = shouldCheck;
if (!shouldCheck) {
return;
}
// Check sensors
check(SensorType::Tps1Primary);
check(SensorType::Tps1Secondary);
check(SensorType::Tps1);
check(SensorType::Tps2Primary);
check(SensorType::Tps2Secondary);
check(SensorType::Tps2);
check(SensorType::AcceleratorPedalPrimary);
check(SensorType::AcceleratorPedalSecondary);
check(SensorType::AcceleratorPedal);
check(SensorType::Map);
check(SensorType::Map2);
check(SensorType::Clt);
check(SensorType::Iat);
check(SensorType::FuelEthanolPercent);
// only bother checking these if we have GPIO chips actually capable of reporting an error
#if BOARD_EXT_GPIOCHIPS > 0 && EFI_PROD_CODE
TunerStudioOutputChannels *state = getTunerStudioOutputChannels();
// Check injectors
#if EFI_ENGINE_CONTROL
int unhappyInjector = 0;
for (size_t i = 0; i < efi::size(enginePins.injectors); i++) {
InjectorOutputPin& pin = enginePins.injectors[i];
// Skip not-configured pins
if (!isBrainPinValid(pin.brainPin)) {
state->injectorDiagnostic[i] = 0;
continue;
}
auto diag = pin.getDiag();
if (diag != PIN_OK && diag != PIN_UNKNOWN) {
unhappyInjector = 1 + i;
auto code = getCodeForInjector(i, diag);
char description[32];
pinDiag2string(description, efi::size(description), diag);
warning(code, "Injector %d fault: %s", i + 1, description);
}
state->injectorDiagnostic[i] = getTSErrorCode(diag);
}
engine->fuelComputer.brokenInjector = unhappyInjector;
engine->fuelComputer.injectorHwIssue = (unhappyInjector != 0);
#endif // EFI_ENGINE_CONTROL
// Check ignition
for (size_t i = 0; i < efi::size(enginePins.injectors); i++) {
IgnitionOutputPin& pin = enginePins.coils[i];
// Skip not-configured pins
if (!isBrainPinValid(pin.brainPin)) {
state->ignitorDiagnostic[i] = 0;
continue;
}
auto diag = pin.getDiag();
if (diag != PIN_OK && diag != PIN_UNKNOWN) {
auto code = getCodeForIgnition(i, diag);
char description[32];
pinDiag2string(description, efi::size(description), diag);
warning(code, "Ignition %d fault: %s", i + 1, description);
}
state->ignitorDiagnostic[i] = getTSErrorCode(diag);
}
#endif // BOARD_EXT_GPIOCHIPS > 0
}
void SensorChecker::onIgnitionStateChanged(bool ignitionOn) {
m_ignitionIsOn = ignitionOn;
if (!ignitionOn) {
// timer keeps track of how long since the state was turned to on (ie, how long ago was it last off)
m_timeSinceIgnOff.reset();
}
}