diff --git a/reference/speeduino.ini b/reference/speeduino.ini index b91e4b86..acc46c92 100644 --- a/reference/speeduino.ini +++ b/reference/speeduino.ini @@ -1055,7 +1055,21 @@ page = 9 #else coolantProtTemp = array, U08, 173, [6], "F", 1.8, -22.23, -40, 215, 0 #endif - unused179_191 = array, U08, 179, [13], "", 1, 0, 0, 255, 0 + + unused179_184 = array, U08, 179, [6], "", 1, 0, 0, 255, 0 + + ; AFR engine protection + afrProtectEnabled = bits, U08, 185, [0:0], "Off", "On" + afrProtectMAP = scalar, U08, 186, "kPa", 2.0, 0.0, 0.0, 511.0, 0 ; 8 bit value, 1 byte + afrProtectRPM = scalar, U08, 187, "RPM", 100, 0.0, 100, 25500, 0 ; 8 bit value, 1 byte + afrProtectTPS = scalar, U08, 188, "%", 0.5, 0.0, 0.0, 100.0, 1 ; 8 bit value, 1 byte +#if LAMBDA + afrProtectDeviation = scalar, U08, 189, "Lambda", {0.1 / stoich}, 0.0, 0.00, 25.5, 2 ; 8 bit value, 1 byte +#else + afrProtectDeviation = scalar, U08, 189, "AFR", 0.1, 0.0, 0.0, 25.5, 1 ; 8 bit value, 1 byte +#endif + afrProtectCutTime = scalar, U08, 190, "seconds", 0.1, 0.0, 0.0, 2.5, 1 ; 8 bit value, 1 byte + afrProtectReactivationTPS = scalar, U08, 191, "%", 0.5, 0.0, 0.0, 100.0, 1 ; 8 bit value, 1 byte page = 10 #if CELSIUS @@ -1571,6 +1585,19 @@ page = 15 #endif defaultValue = vvtDelay, 60 + ; AFR protection default values + defaultValue = afrProtectEnabled, 0 + defaultValue = afrProtectMAP, 180 + defaultValue = afrProtectRPM, 4000 + defaultValue = afrProtectTPS, 80 +#if LAMBDA + defaultValue = afrProtectDeviation, 0.10 +#else + defaultValue = afrProtectDeviation, 1.47 +#endif + defaultValue = afrProtectCutTime, 0.8 + defaultValue = afrProtectReactivationTPS, 20 + ;Default pins defaultValue = fanPin, 0 defaultValue = vvt1Pin, 0 @@ -1757,6 +1784,7 @@ menuDialog = main groupChildMenu = revLimiterDialog, "Rev Limiters", { engineProtectType } groupChildMenu = boostCut, "Boost Cut", { engineProtectType } groupChildMenu = oilPressureProtection, "Oil Pressure", { engineProtectType } + groupChildMenu = afrProtect, "AFR Protection", { engineProtectType } subMenu = flexFuel, "Flex Fuel", 2 subMenu = veTableDialog, "VE Table", 0 subMenu = sparkTbl, "Spark Table", 2 @@ -1995,6 +2023,18 @@ menuDialog = main hardRevLim = "A fixed hard rev limit is a single point that the fuel or ignition (or both) will be cut completely to reduce increasing RPMs" engineProtectMaxRPM = "The RPM point that engine protections will engage from. Below this RPM value, engine protections will NOT be active" + ; AFR Protection Help + afrProtectMAP = "Minimum manifold air pressure the AFR lean protection will activate" + afrProtectRPM = "The minimum RPM the AFR lean protection will activate" + afrProtectTPS = "The minimum current throttle position for the AFR lean protection to activate" +#if LAMBDA + afrProtectDeviation = "Maximum deviation from current lambda value in which lean protection will activate" +#else + afrProtectDeviation = "Maximum deviation from current AFR value in which lean protection will activate" +#endif + afrProtectCutTime = "A time delay before activating engine protection when all conditions has been met" + afrProtectReactivationTPS = "Going below this throttle position (%) will deactivate this protection" + fuel2InputPin = "The Arduino pin that is being used to trigger the second fuel table to be active" fuel2InputPolarity = "Whether the 2nd fuel table should be active when input is high or low. This should be LOW for a typical ground switching input" fuel2InputPullup = "Whether to use the built in PULLUP for the switching input. This should be Yes for a typical ground switching input" @@ -2818,7 +2858,23 @@ menuDialog = main field = "Oil Pressure Protection", oilPressureProtEnbl, { oilPressureEnable } panel = oil_pressure_prot_curve, { oilPressureEnable && oilPressureProtEnbl } - + ; AFR engine protection dialog + dialog = afrProtect, "AFR Protection", yAxis + field = "AFR protection is used to prevent engine from running lean" + field = "#Note: This function requires wideband sensor and proper AFR table" + field = "" + field = "Enable AFR protection ", afrProtectEnabled, {egoType == 2} + field = "Minimum manifold air pressure ", afrProtectMAP, {afrProtectEnabled} + field = "Minimum engine RPM ", afrProtectRPM, {afrProtectEnabled} + field = "Minimum throttle position ", afrProtectTPS, {afrProtectEnabled} +#if LAMBDA + field = "Maximum lambda deviation ", afrProtectDeviation, {afrProtectEnabled} +#else + field = "Maximum AFR deviation ", afrProtectDeviation, {afrProtectEnabled} +#endif + field = "Time before cut ", afrProtectCutTime, {afrProtectEnabled} + field = "" + field = "Reactivate below throttle ", afrProtectReactivationTPS, {afrProtectEnabled} indicatorPanel = protectIndicatorPanel, 1, { 1 } indicator = { engineProtectStatus}, "Engine Protect OFF", "Engine Protect ON", green, black, red, black diff --git a/speeduino/engineProtection.h b/speeduino/engineProtection.h index 09a05b43..d98f5312 100644 --- a/speeduino/engineProtection.h +++ b/speeduino/engineProtection.h @@ -2,7 +2,6 @@ #define HARD_REV_FIXED 1 #define HARD_REV_COOLANT 2 - byte checkEngineProtect(); byte checkRevLimit(); byte checkBoostLimit(); diff --git a/speeduino/engineProtection.ino b/speeduino/engineProtection.ino index 5a87ed5a..b69f09cc 100644 --- a/speeduino/engineProtection.ino +++ b/speeduino/engineProtection.ino @@ -112,7 +112,85 @@ byte checkOilPressureLimit() byte checkAFRLimit() { - byte checkAFRLimitActive = 0; + static bool checkAFRLimitActive = false; + static bool afrProtectCountEnabled = false; + static unsigned long afrProtectCount = 0; + static constexpr char X2_MULTIPLIER = 2; + static constexpr char X100_MULTIPLIER = 100; + + /* + To use this function, a wideband sensor is required. + + First of all, check whether engine protection is enabled, + thereafter check whether AFR protection is enabled and at last + if wideband sensor is used. + + After confirmation, the following conditions has to be met: + - MAP above x kPa + - RPM above x + - TPS above x % + - AFR threshold (AFR target + defined maximum deviation) + - Time before cut + + See afrProtect variables in globals.h for more information. + + If all conditions above are true, a specified time delay is starting + to count down in which leads to the engine protection function + to be activated using selected protection cut method (e.g. ignition, + fuel or both). + + For reactivation, the following condition has to be met: + - TPS below x % + */ + + /* + Do 3 checks here; + - whether engine protection is enabled + - whether AFR protection is enabled + - whether wideband sensor is used + */ + if(configPage6.engineProtectType != PROTECT_CUT_OFF && configPage9.afrProtectEnabled && configPage6.egoType == EGO_TYPE_WIDE) { + /* Conditions */ + bool mapCondition = (currentStatus.MAP >= (configPage9.afrProtectMinMAP * X2_MULTIPLIER)) ? true : false; + bool rpmCondition = (currentStatus.RPMdiv100 >= configPage9.afrProtectMinRPM) ? true : false; + bool tpsCondition = (currentStatus.TPS >= configPage9.afrProtectMinTPS) ? true : false; + bool afrCondition = (currentStatus.O2 >= (currentStatus.afrTarget + configPage9.afrProtectDeviation)) ? true : false; + + /* Check if conditions above are fulfilled */ + if(mapCondition && rpmCondition && tpsCondition && afrCondition) + { + /* All conditions fulfilled - start counter for 'protection delay' */ + if(!afrProtectCountEnabled) + { + afrProtectCountEnabled = true; + afrProtectCount = millis(); + } + + /* Check if countdown has reached its target, if so then instruct to cut */ + if(millis() >= (afrProtectCount + (configPage9.afrProtectCutTime * X100_MULTIPLIER))) + { + checkAFRLimitActive = true; + BIT_SET(currentStatus.engineProtectStatus, ENGINE_PROTECT_BIT_AFR); + } + } + else + { + /* Conditions have presumably changed - deactivate and reset counter */ + if(afrProtectCountEnabled) + { + afrProtectCountEnabled = false; + afrProtectCount = 0; + } + } + + /* Check if condition for reactivation is fulfilled */ + if(checkAFRLimitActive && (currentStatus.TPS <= configPage9.afrProtectReactivationTPS)) + { + checkAFRLimitActive = false; + afrProtectCountEnabled = false; + BIT_CLEAR(currentStatus.engineProtectStatus, ENGINE_PROTECT_BIT_AFR); + } + } return checkAFRLimitActive; } diff --git a/speeduino/globals.h b/speeduino/globals.h index 08a805bf..bf6a55e1 100644 --- a/speeduino/globals.h +++ b/speeduino/globals.h @@ -248,6 +248,10 @@ #define COMPOSITE_LOG_TRIG 2 #define COMPOSITE_LOG_SYNC 3 +#define EGO_TYPE_OFF 0 +#define EGO_TYPE_NARROW 1 +#define EGO_TYPE_WIDE 2 + #define INJ_TYPE_PORT 0 #define INJ_TYPE_TBODY 1 @@ -1180,13 +1184,13 @@ struct config9 { byte unused10_182; byte unused10_183; byte unused10_184; - byte unused10_185; - byte unused10_186; - byte unused10_187; - byte unused10_188; - byte unused10_189; - byte unused10_190; - byte unused10_191; + byte afrProtectEnabled : 1; /* < AFR protection enabled status. 0 = disabled, 1 = enabled */ + byte afrProtectMinMAP; /* < Minimum MAP. Stored value is divided by 2. Increments of 2 kPa, maximum 511 (?) kPa */ + byte afrProtectMinRPM; /* < Minimum RPM. Stored value is divded by 100. Increments of 100 RPM, maximum 25500 RPM */ + byte afrProtectMinTPS; /* < Minimum TPS. */ + byte afrProtectDeviation; /* < Maximum deviation from AFR target table. Stored value is multiplied by 10 */ + byte afrProtectCutTime; /* < Time in ms before cut. Stored value is divided by 100. Maximum of 2550 ms */ + byte afrProtectReactivationTPS; /* Disable engine protection cut once below this TPS percentage */ #if defined(CORE_AVR) }; diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index e7d44364..997e3125 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -1328,7 +1328,7 @@ uint16_t PW(int REQ_FUEL, byte VE, long MAP, uint16_t corrections, int injOpen) if ( configPage2.multiplyMAP == MULTIPLY_MAP_MODE_100) { iMAP = ((unsigned int)MAP << 7) / 100; } else if( configPage2.multiplyMAP == MULTIPLY_MAP_MODE_BARO) { iMAP = ((unsigned int)MAP << 7) / currentStatus.baro; } - if ( (configPage2.includeAFR == true) && (configPage6.egoType == 2) && (currentStatus.runSecs > configPage6.ego_sdelay) ) { + if ( (configPage2.includeAFR == true) && (configPage6.egoType == EGO_TYPE_WIDE) && (currentStatus.runSecs > configPage6.ego_sdelay) ) { iAFR = ((unsigned int)currentStatus.O2 << 7) / currentStatus.afrTarget; //Include AFR (vs target) if enabled } if ( (configPage2.incorporateAFR == true) && (configPage2.includeAFR == false) ) { @@ -1341,7 +1341,7 @@ uint16_t PW(int REQ_FUEL, byte VE, long MAP, uint16_t corrections, int injOpen) unsigned long intermediate = ((uint32_t)REQ_FUEL * (uint32_t)iVE) >> 7; //Need to use an intermediate value to avoid overflowing the long if ( configPage2.multiplyMAP > 0 ) { intermediate = (intermediate * (unsigned long)iMAP) >> 7; } - if ( (configPage2.includeAFR == true) && (configPage6.egoType == 2) && (currentStatus.runSecs > configPage6.ego_sdelay) ) { + if ( (configPage2.includeAFR == true) && (configPage6.egoType == EGO_TYPE_WIDE) && (currentStatus.runSecs > configPage6.ego_sdelay) ) { //EGO type must be set to wideband and the AFR warmup time must've elapsed for this to be used intermediate = (intermediate * (unsigned long)iAFR) >> 7; } diff --git a/speeduino/updates.ino b/speeduino/updates.ino index 264a2b07..79a04821 100644 --- a/speeduino/updates.ino +++ b/speeduino/updates.ino @@ -659,6 +659,13 @@ void doUpdates() ++table_Y; } + //AFR Protection added, add default values + configPage9.afrProtectEnabled = 0; //Disable by default + configPage9.afrProtectMinMAP = 90; //Is divided by 2, vlue represents 180kPa + configPage9.afrProtectMinRPM = 40; //4000 RPM min + configPage9.afrProtectMinTPS = 160; //80% TPS min + configPage9.afrProtectDeviation = 14; //1.4 AFR deviation + writeAllConfig(); storeEEPROMVersion(20); }