implement sensor checker (#4395)

* implement sensor checker

* use default

* s

* fix

* properly report non-timeout errors

* it's not safe to read Value when invalid

* it's not safe to read Value when invalid
This commit is contained in:
Matthew Kennedy 2022-07-30 14:08:48 -07:00 committed by GitHub
parent d7802e6373
commit b6f1779781
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 259 additions and 43 deletions

View File

@ -473,33 +473,33 @@ extern HIP9011 instance;
static void updateTempSensors() {
SensorResult clt = Sensor::get(SensorType::Clt);
engine->outputChannels.coolant = clt.Value;
engine->outputChannels.coolant = clt.value_or(0);
engine->outputChannels.isCltError = !clt.Valid;
SensorResult iat = Sensor::get(SensorType::Iat);
engine->outputChannels.intake = iat.Value;
engine->outputChannels.intake = iat.value_or(0);
engine->outputChannels.isIatError = !iat.Valid;
SensorResult auxTemp1 = Sensor::get(SensorType::AuxTemp1);
engine->outputChannels.auxTemp1 = auxTemp1.Value;
engine->outputChannels.auxTemp1 = auxTemp1.value_or(0);
SensorResult auxTemp2 = Sensor::get(SensorType::AuxTemp2);
engine->outputChannels.auxTemp2 = auxTemp2.Value;
engine->outputChannels.auxTemp2 = auxTemp2.value_or(0);
}
static void updateThrottles() {
SensorResult tps1 = Sensor::get(SensorType::Tps1);
engine->outputChannels.TPSValue = tps1.Value;
engine->outputChannels.TPSValue = tps1.value_or(0);
engine->outputChannels.isTpsError = !tps1.Valid;
engine->outputChannels.tpsADC = convertVoltageTo10bitADC(Sensor::getRaw(SensorType::Tps1Primary));
SensorResult tps2 = Sensor::get(SensorType::Tps2);
engine->outputChannels.TPS2Value = tps2.Value;
engine->outputChannels.TPS2Value = tps2.value_or(0);
// If we don't have a TPS2 at all, don't turn on the failure light
engine->outputChannels.isTps2Error = !tps2.Valid && Sensor::hasSensor(SensorType::Tps2Primary);
SensorResult pedal = Sensor::get(SensorType::AcceleratorPedal);
engine->outputChannels.throttlePedalPosition = pedal.Value;
engine->outputChannels.throttlePedalPosition = pedal.value_or(0);
// Only report fail if you have one (many people don't)
engine->outputChannels.isPedalError = !pedal.Valid && Sensor::hasSensor(SensorType::AcceleratorPedalPrimary);
@ -525,11 +525,11 @@ static void updateLambda() {
static void updateFuelSensors() {
// Low pressure is directly in kpa
engine->outputChannels.lowFuelPressure = Sensor::get(SensorType::FuelPressureLow).Value;
engine->outputChannels.lowFuelPressure = Sensor::get(SensorType::FuelPressureLow).value_or(0);
// High pressure is in bar, aka 100 kpa
engine->outputChannels.highFuelPressure = KPA2BAR(Sensor::get(SensorType::FuelPressureHigh).Value);
engine->outputChannels.highFuelPressure = KPA2BAR(Sensor::get(SensorType::FuelPressureHigh).value_or(0));
engine->outputChannels.flexPercent = Sensor::get(SensorType::FuelEthanolPercent).Value;
engine->outputChannels.flexPercent = Sensor::get(SensorType::FuelEthanolPercent).value_or(0);
engine->outputChannels.fuelTankLevel = Sensor::getOrZero(SensorType::FuelLevel);
}
@ -582,10 +582,10 @@ static void updateRawSensors() {
static void updatePressures() {
engine->outputChannels.baroPressure = Sensor::getOrZero(SensorType::BarometricPressure);
engine->outputChannels.MAPValue = Sensor::getOrZero(SensorType::Map);
engine->outputChannels.oilPressure = Sensor::get(SensorType::OilPressure).Value;
engine->outputChannels.oilPressure = Sensor::get(SensorType::OilPressure).value_or(0);
engine->outputChannels.auxLinear1 = Sensor::get(SensorType::AuxLinear1).Value;
engine->outputChannels.auxLinear2 = Sensor::get(SensorType::AuxLinear2).Value;
engine->outputChannels.auxLinear1 = Sensor::get(SensorType::AuxLinear1).value_or(0);
engine->outputChannels.auxLinear2 = Sensor::get(SensorType::AuxLinear2).value_or(0);
}
static void updateMiscSensors() {
@ -715,7 +715,7 @@ static void updateTpsDebug() {
void updateTunerStudioState() {
TunerStudioOutputChannels *tsOutputChannels = &engine->outputChannels;
#if EFI_SHAFT_POSITION_INPUT
int rpm = Sensor::get(SensorType::Rpm).Value;
int rpm = Sensor::get(SensorType::Rpm).value_or(0);
#else /* EFI_SHAFT_POSITION_INPUT */
int rpm = 0;
#endif /* EFI_SHAFT_POSITION_INPUT */

View File

@ -196,7 +196,7 @@ bool EtbController::init(etb_function_e function, DcMotor *motor, pid_s *pidPara
if (!Sensor::isRedundant(m_positionSensor)) {
firmwareError(
OBD_Throttle_Position_Sensor_Circuit_Malfunction,
OBD_TPS_Configuration,
"Use of electronic throttle requires %s to be redundant.",
Sensor::getSensorName(m_positionSensor)
);
@ -206,7 +206,7 @@ bool EtbController::init(etb_function_e function, DcMotor *motor, pid_s *pidPara
if (!Sensor::isRedundant(SensorType::AcceleratorPedal)) {
firmwareError(
OBD_Throttle_Position_Sensor_Circuit_Malfunction,
OBD_TPS_Configuration,
"Use of electronic throttle requires accelerator pedal to be redundant."
);
@ -650,7 +650,7 @@ struct EtbImpl final : public TBase {
// Check that the calibrate actually moved the throttle
if (absF(primaryMax - primaryMin) < 0.5f) {
firmwareError(OBD_Throttle_Position_Sensor_Circuit_Malfunction, "Auto calibrate failed, check your wiring!\r\nClosed voltage: %.1fv Open voltage: %.1fv", primaryMin, primaryMax);
firmwareError(OBD_TPS_Configuration, "Auto calibrate failed, check your wiring!\r\nClosed voltage: %.1fv Open voltage: %.1fv", primaryMin, primaryMax);
TBase::m_isAutocal = false;
return;
}

View File

@ -13,8 +13,8 @@ bool FanController::getState(bool acActive, bool lastState) {
disabledWhileEngineStopped = notRunning && disableWhenStopped();
brokenClt = !clt;
enabledForAc = enableWithAc() && acActive;
hot = clt.Value > getFanOnTemp();
cold = clt.Value < getFanOffTemp();
hot = clt.value_or(0) > getFanOnTemp();
cold = clt.value_or(0) < getFanOffTemp();
if (cranking) {
// Inhibit while cranking

View File

@ -40,6 +40,7 @@
#include "gear_detector.h"
#include "advance_map.h"
#include "fan_control.h"
#include "sensor_checker.h"
#ifndef EFI_UNIT_TEST
#error EFI_UNIT_TEST must be defined!
@ -192,6 +193,7 @@ public:
GearDetector,
#endif // EFI_VEHICLE_SPEED
KnockController,
SensorChecker,
EngineModule // dummy placeholder so the previous entries can all have commas
> engineModules;

View File

@ -18,6 +18,7 @@ extern "C"
#endif /* __cplusplus */
typedef enum {
OBD_None = 0,
//P0001 Fuel Volume Regulator Control Circuit/Open
//P0002 Fuel Volume Regulator Control Circuit Range/Performance
//P0003 Fuel Volume Regulator Control Circuit Low
@ -129,30 +130,39 @@ typedef enum {
//P0104 Mass or Volume Air Flow Circuit Intermittent
OBD_Manifold_Absolute_Pressure_Circuit_Malfunction = 105,
//P0106 Manifold Absolute Pressure/Barometric Pressure Circuit Range/Performance Problem
OBD_Map_Timeout = 106,
OBD_Map_Low = 107,
OBD_Map_High = 108,
//P0107 Manifold Absolute Pressure/Barometric Pressure Circuit Low Input
//P0108 Manifold Absolute Pressure/Barometric Pressure Circuit High Input
//P0109 Manifold Absolute Pressure/Barometric Pressure Circuit Intermittent
/**
* We raise intake error code if IAT is calculated below -50C or above +100C
*/
OBD_Intake_Air_Temperature_Circuit_Malfunction = 110,
OBD_ThermistorConfig = 111,
OBD_Iat_Timeout = 110,
OBD_Iat_Low = 112,
OBD_Iat_High = 113,
//P0111 Intake Air Temperature Circuit Range/Performance Problem
//P0112 Intake Air Temperature Circuit Low Input
//P0113 Intake Air Temperature Circuit High Input
//P0114 Intake Air Temperature Circuit Intermittent
OBD_Engine_Coolant_Temperature_Circuit_Malfunction = 115,
OBD_Clt_Timeout = 115,
OBD_Clt_Low = 117,
OBD_Clt_High = 118,
//P0116 Engine Coolant Temperature Circuit Range/Performance Problem
//P0117 Engine Coolant Temperature Circuit Low Input
//P0118 Engine Coolant Temperature Circuit High Input
//P0119 Engine Coolant Temperature Circuit Intermittent
/**
* See also tpsErrorDetectionTooLow
*/
OBD_Throttle_Position_Sensor_Circuit_Malfunction = 120,
/**
* See also tpsErrorDetectionTooHigh
*/
OBD_Throttle_Position_Sensor_Range_Performance_Problem = 121,
OBD_TPS_Configuration = 121,
OBD_TPS1_Primary_Timeout = 120,
OBD_TPS1_Primary_Low = 122,
OBD_TPS1_Primary_High = 123,
//P0122 Throttle Position Sensor/Switch A Circuit Low Input
//P0123 Throttle Position Sensor/Switch A Circuit High Input
//P0124 Throttle Position Sensor/Switch A Circuit Intermittent
@ -201,6 +211,11 @@ typedef enum {
//P0173 Fuel Trim Malfunction (Bank 2)
//P0174 System too Lean (Bank 2)
//P0175 System too Rich (Bank 2)
OBD_FlexSensor_Timeout = 176,
OBD_FlexSensor_Low = 178,
OBD_FlexSensor_High = 179,
//P0176 Fuel Composition Sensor Circuit Malfunction
//P0177 Fuel Composition Sensor Circuit Range/Performance
//P0178 Fuel Composition Sensor Circuit Low Input
@ -248,6 +263,15 @@ typedef enum {
//P0219 Engine Overspeed Condition
//P0220 Throttle/Petal Position Sensor/Switch B Circuit Malfunction
//P0221 Throttle/Petal Position Sensor/Switch B Circuit Range/Performance Problem
OBD_TPS1_Secondary_Timeout = 220,
OBD_TPS1_Secondary_Low = 222,
OBD_TPS1_Secondary_High = 223,
OBD_TPS2_Primary_Timeout = 225,
OBD_TPS2_Primary_Low = 227,
OBD_TPS2_Primary_High = 228,
//P0222 Throttle/Petal Position Sensor/Switch B Circuit Low Input
//P0223 Throttle/Petal Position Sensor/Switch B Circuit High Input
//P0224 Throttle/Petal Position Sensor/Switch B Circuit Intermittent
@ -1044,6 +1068,19 @@ typedef enum {
//P2117 Throttle/Pedal Pos Sensor F Minimum Stop Perf
//P2118 Throttle Actuator Ctrl Motor Current Range/Perf
//P2119 Throttle Actuator Ctrl Throttle Body Range/Perf
OBD_TPS2_Secondary_Timeout = 2120,
OBD_TPS2_Secondary_Low = 2122,
OBD_TPS2_Secondary_High = 2123,
OBD_PPS_Primary_Timeout = 2125,
OBD_PPS_Primary_Low = 2127,
OBD_PPS_Primary_High = 2128,
OBD_PPS_Secondary_Timeout = 2130,
OBD_PPS_Secondary_Low = 2132,
OBD_PPS_Secondary_High = 2133,
//P2120 Throttle/Pedal Pos Sensor/Switch D Circ
//P2121 Throttle/Pedal Pos Sensor/Switch D Circ Range/Perf
//P2122 Throttle/Pedal Pos Sensor/Switch D Circ Low Input
@ -1059,6 +1096,9 @@ typedef enum {
//P2132 Throttle/Pedal Pos Sensor/Switch F Circ Low Input
//P2133 Throttle/Pedal Pos Sensor/Switch F Circ High Input
//P2134 Throttle/Pedal Pos Sensor/Switch F Circ Interm
OBD_TPS1_Correlation = 2135,
OBD_TPS2_Correlation = 2136,
OBD_PPS_Correlation = 2136,
//P2135 Throttle/Pedal Pos Sensor/Switch A / B Voltage Correlation
//P2136 Throttle/Pedal Pos Sensor/Switch A / C Voltage Correlation
//P2137 Throttle/Pedal Pos Sensor/Switch B / C Voltage Correlation

View File

@ -0,0 +1,157 @@
#include "pch.h"
// Decode what OBD code we should use for a particular [sensor, code] problem
static obd_code_e getCode(SensorType type, UnexpectedCode code) {
switch (type) {
case SensorType::Tps1:
case SensorType::Tps1Primary:
switch (code) {
case UnexpectedCode::Timeout: return OBD_TPS1_Primary_Timeout;
case UnexpectedCode::Low: return OBD_TPS1_Primary_Low;
case UnexpectedCode::High: return OBD_TPS1_Primary_High;
case UnexpectedCode::Inconsistent: return OBD_TPS1_Correlation;
default: break;
} break;
case SensorType::Tps1Secondary:
switch (code) {
case UnexpectedCode::Timeout: return OBD_TPS1_Secondary_Timeout;
case UnexpectedCode::Low: return OBD_TPS1_Secondary_Low;
case UnexpectedCode::High: return OBD_TPS1_Secondary_High;
default: break;
} break;
case SensorType::Tps2:
case SensorType::Tps2Primary:
switch (code) {
case UnexpectedCode::Timeout: return OBD_TPS2_Primary_Timeout;
case UnexpectedCode::Low: return OBD_TPS2_Primary_Low;
case UnexpectedCode::High: return OBD_TPS2_Primary_High;
case UnexpectedCode::Inconsistent: return OBD_TPS2_Correlation;
default: break;
} break;
case SensorType::Tps2Secondary:
switch (code) {
case UnexpectedCode::Timeout: return OBD_TPS2_Secondary_Timeout;
case UnexpectedCode::Low: return OBD_TPS2_Secondary_Low;
case UnexpectedCode::High: return OBD_TPS2_Secondary_High;
default: break;
} break;
case SensorType::AcceleratorPedal:
case SensorType::AcceleratorPedalPrimary:
switch (code) {
case UnexpectedCode::Timeout: return OBD_PPS_Primary_Timeout;
case UnexpectedCode::Low: return OBD_PPS_Primary_Low;
case UnexpectedCode::High: return OBD_PPS_Primary_High;
case UnexpectedCode::Inconsistent: return OBD_PPS_Correlation;
default: break;
} break;
case SensorType::AcceleratorPedalSecondary:
switch (code) {
case UnexpectedCode::Timeout: return OBD_PPS_Secondary_Timeout;
case UnexpectedCode::Low: return OBD_PPS_Secondary_Low;
case UnexpectedCode::High: return OBD_PPS_Secondary_High;
default: break;
} break;
case SensorType::Map:
switch (code) {
case UnexpectedCode::Timeout: return OBD_Map_Timeout;
case UnexpectedCode::Low: return OBD_Map_Low;
case UnexpectedCode::High: return OBD_Map_High;
default: break;
} break;
case SensorType::Clt:
switch (code) {
case UnexpectedCode::Timeout: return OBD_Clt_Timeout;
case UnexpectedCode::Low: return OBD_Clt_Low;
case UnexpectedCode::High: return OBD_Clt_High;
default: break;
} break;
case SensorType::Iat:
switch (code) {
case UnexpectedCode::Timeout: return OBD_Iat_Timeout;
case UnexpectedCode::Low: return OBD_Iat_Low;
case UnexpectedCode::High: return OBD_Iat_High;
default: break;
} break;
case SensorType::FuelEthanolPercent:
switch (code) {
case UnexpectedCode::Timeout: return OBD_FlexSensor_Timeout;
case UnexpectedCode::Low: return OBD_FlexSensor_Low;
case UnexpectedCode::High: return OBD_FlexSensor_High;
default: break;
} break;
default:
break;
}
return OBD_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;
}
obd_code_e code = getCode(type, result.Code);
if (code != OBD_None) {
warning(code, "Sensor fault: %s %s", Sensor::getSensorName(type), describeUnexpected(result.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.
if (!m_ignitionIsOn || !m_timeSinceIgnOff.hasElapsedSec(5)) {
return;
}
check(SensorType::Tps1Primary);
check(SensorType::Tps1Secondary);
check(SensorType::Tps1);
check(SensorType::Tps2Primary);
check(SensorType::Tps2Secondary);
check(SensorType::Tps2);
check(SensorType::Tps2);
check(SensorType::Tps2Primary);
check(SensorType::Tps2Secondary);
check(SensorType::Map);
check(SensorType::Clt);
check(SensorType::Iat);
check(SensorType::FuelEthanolPercent);
}
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();
}
}

View File

@ -0,0 +1,11 @@
#pragma once
struct SensorChecker : public EngineModule {
public:
void onSlowCallback() override;
void onIgnitionStateChanged(bool ignitionOn) override;
private:
bool m_ignitionIsOn = false;
Timer m_timeSinceIgnOff;
};

View File

@ -8,6 +8,7 @@ CONTROLLERS_SENSORS_SRC_CPP = $(PROJECT_DIR)/controllers/sensors/thermistors.cp
$(PROJECT_DIR)/controllers/sensors/ego.cpp \
$(PROJECT_DIR)/controllers/sensors/sensor.cpp \
$(PROJECT_DIR)/controllers/sensors/sensor_info_printing.cpp \
$(PROJECT_DIR)/controllers/sensors/sensor_checker.cpp \
$(PROJECT_DIR)/controllers/sensors/functional_sensor.cpp \
$(PROJECT_DIR)/controllers/sensors/redundant_sensor.cpp \
$(PROJECT_DIR)/controllers/sensors/redundant_ford_tps.cpp \

View File

@ -37,6 +37,11 @@ public:
return result;
}
// Result is already failed, return that so that we get the real error code instead of a timeout
if (!result) {
return result;
}
if (m_timeoutPeriod != 0) { // zero m_timeoutPeriod means value lasts forever
if (getTimeNowNt() - m_timeoutPeriod > m_lastUpdate) {
return UnexpectedCode::Timeout;

View File

@ -211,7 +211,7 @@ static void printTpsSenser(const char *msg, SensorType sensor, int16_t min, int1
raw, getPinNameByAdcChannel(msg, channel, pinNameBuffer));
efiPrintf("current 10bit=%d value=%.2f", convertVoltageTo10bitADC(raw), tps.Value);
efiPrintf("current 10bit=%d value=%.2f", convertVoltageTo10bitADC(raw), tps.value_or(0));
}
void printTPSInfo(void) {

View File

@ -26,7 +26,7 @@ static FuncPair fclt, fiat, faux1, faux2;
static void validateThermistorConfig(const char *msg, thermistor_conf_s& cfg) {
if (cfg.tempC_1 >= cfg.tempC_2 ||
cfg.tempC_2 >= cfg.tempC_3) {
firmwareError(OBD_Engine_Coolant_Temperature_Circuit_Malfunction, "Invalid thermistor %s configuration: please check that temperatures are in the ascending order %f %f %f",
firmwareError(OBD_ThermistorConfig, "Invalid thermistor %s configuration: please check that temperatures are in the ascending order %f %f %f",
msg,
cfg.tempC_1,
cfg.tempC_2,

View File

@ -62,7 +62,7 @@ private:
// If the voltage for closed vs. open is very near, something is wrong with your calibration
if (split < 0.5f) {
firmwareError(OBD_Throttle_Position_Sensor_Circuit_Malfunction, "\"%s\" problem: open %.2f/closed %.2f cal values are too close together. Check your calibration and wiring!", name(),
firmwareError(OBD_TPS_Configuration, "\"%s\" problem: open %.2f/closed %.2f cal values are too close together. Check your calibration and wiring!", name(),
cfg.open,
cfg.closed);
return false;
@ -105,7 +105,7 @@ public:
bool tooCloseOpen = absF(primary.open - secondary.open) < 0.2f;
if (hasBothSensors && tooCloseClosed && tooCloseOpen) {
firmwareError(OBD_Throttle_Position_Sensor_Circuit_Malfunction, "Configuration for redundant pair %s/%s are too similar - did you wire one sensor to both inputs...?", m_pri.name(), m_sec.name());
firmwareError(OBD_TPS_Configuration, "Configuration for redundant pair %s/%s are too similar - did you wire one sensor to both inputs...?", m_pri.name(), m_sec.name());
return;
}
}

View File

@ -140,15 +140,15 @@ static void testMalfunctionCentralRemoveNonExistent() {
clearWarnings();
// this should not crash
removeError(OBD_Engine_Coolant_Temperature_Circuit_Malfunction);
removeError(OBD_TPS1_Correlation);
}
static void testMalfunctionCentralSameElementAgain() {
clearWarnings();
error_codes_set_s localCopy;
addError(OBD_Engine_Coolant_Temperature_Circuit_Malfunction);
addError(OBD_Engine_Coolant_Temperature_Circuit_Malfunction);
addError(OBD_TPS1_Correlation);
addError(OBD_TPS1_Correlation);
getErrorCodes(&localCopy);
ASSERT_EQ(1, localCopy.count);
}
@ -157,10 +157,10 @@ static void testMalfunctionCentralRemoveFirstElement() {
clearWarnings();
error_codes_set_s localCopy;
obd_code_e firstElement = OBD_Engine_Coolant_Temperature_Circuit_Malfunction;
obd_code_e firstElement = OBD_TPS1_Correlation;
addError(firstElement);
obd_code_e secondElement = OBD_Intake_Air_Temperature_Circuit_Malfunction;
obd_code_e secondElement = OBD_TPS2_Correlation;
addError(secondElement);
getErrorCodes(&localCopy);
ASSERT_EQ(2, localCopy.count);
@ -186,7 +186,7 @@ TEST(misc, testMalfunctionCentral) {
getErrorCodes(&localCopy);
ASSERT_EQ(0, localCopy.count);
obd_code_e code = OBD_Engine_Coolant_Temperature_Circuit_Malfunction;
obd_code_e code = OBD_TPS1_Correlation;
// let's add one error and validate
addError(code);
@ -200,7 +200,7 @@ TEST(misc, testMalfunctionCentral) {
ASSERT_EQ(1, localCopy.count);
ASSERT_EQ(code, localCopy.error_codes[0]);
code = OBD_Intake_Air_Temperature_Circuit_Malfunction;
code = OBD_TPS2_Correlation;
addError(code);
getErrorCodes(&localCopy);
// todo: ASSERT_EQ(2, localCopy.count);