diff --git a/lambda-test/avrjunit.h b/lambda-test/avrjunit.h index 597930b..1d0fef3 100644 --- a/lambda-test/avrjunit.h +++ b/lambda-test/avrjunit.h @@ -25,6 +25,16 @@ #define assertFalse(exp) if (exp) return false /** + * Returns false if the given expression is not NULL. + */ +#define assertNull(exp) if ((exp) != NULL) return false + +/** + * Returns false if the given expression is NULL. + */ +#define assertNotNull(exp) if ((exp) == NULL) return false + +/** * Function pointer for test functions taking no parameters and returning * true on success and false on failure. */ diff --git a/lambda-test/rules-test.c b/lambda-test/rules-test.c index fba85dc..fd45c15 100644 --- a/lambda-test/rules-test.c +++ b/lambda-test/rules-test.c @@ -8,6 +8,7 @@ * */ +#include "string.h" #include "avrjunit.h" #include "rules.h" #include "alert.h" @@ -389,49 +390,49 @@ age = 0; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == none); meas.tempI = 9; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == none); meas.tempI = 100; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == 1); + assertTrue(dir == firing_up); meas.tempI = 790; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == 1); + assertTrue(dir == firing_up); meas.tempI = 790; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == none); meas.tempI = 800; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == 1); + assertTrue(dir == firing_up); meas.tempI = 850; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == none); meas.tempI = 900; meas.lambda = 1999; age = 180; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == burning); cancelAlert(false); @@ -445,49 +446,51 @@ age = 0; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == none); meas.tempI = 900; meas.lambda = 1999; age = 180; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == burning); meas.tempI = 800; meas.lambda = 1999; age = 180; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == burning); meas.tempI = 750; meas.lambda = 1999; age = 180; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == burning); meas.tempI = 700; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == -1); + assertTrue(dir == burning_down); meas.tempI = 700; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == 0); + assertTrue(dir == none); meas.tempI = 100; meas.lambda = 2000; age = 180; reason(meas); - assertTrue(dir == -1); + assertTrue(dir == burning_down); cancelAlert(false); return true; } +// TODO add tests for "burning" and "warm start" + /* Test "class" */ static const char class[] PROGMEM = "rules"; diff --git a/lambda/command.c b/lambda/command.c index 7c2f589..4df94b6 100644 --- a/lambda/command.c +++ b/lambda/command.c @@ -101,6 +101,9 @@ // assuming one measurement was logged per second addTime(1); Measurement meas = readMeas(fields, fieldCount); + if (getHeaterState() == heaterStateOff) { + meas.lambda = 2000; // Lambda 2.00 + } if (getHeaterState() == heaterStateOff || getHeaterState() == heaterStateReady) { updateMeas(meas); diff --git a/lambda/display.c b/lambda/display.c index a3c12bc..6d1ad5f 100644 --- a/lambda/display.c +++ b/lambda/display.c @@ -134,6 +134,7 @@ char* hint; switch (getDir()) { case firing_up: hint = " >"; break; + case burning: hint = " ^"; break; case burning_down: hint = " <"; break; default: hint = " -"; break; } diff --git a/lambda/rules.c b/lambda/rules.c index 2219a83..bc69296 100644 --- a/lambda/rules.c +++ b/lambda/rules.c @@ -5,6 +5,7 @@ * Author: dode@luniks.net */ +#include #include #include "alert.h" #include "integers.h" @@ -12,29 +13,22 @@ #include "rules.h" #include "messages.h" -#define BEEPS 30 -#define LENGTH 10 -#define TONE 31 - uint8_t age = 0; FireDir dir = none; uint8_t airgate = 100; static Measurement rulesMeasMax = {0, 0, 2000, 0}; static Measurement rulesMeasPrev = {0, 0, 2000, 0}; -static bool measPrevInit = false; - -int8_t getDir(void) { - return dir; -} +static bool prevInit = false; /** - * Reminds to set the air gate to 50% when the fire is still building up - * and the temperature has reached 500°C. + * Reminds to set the air gate to 50% when the fire is still firing up + * and the temperature has reached TEMP_AIRGATE_50. */ static void airgate50(bool* const fired, int8_t const dir, Measurement const meas) { - if (! *fired && dir == firing_up && meas.tempI >= 500) { + if (! *fired && dir == firing_up && + meas.tempI >= TEMP_AIRGATE_50) { airgate = 50; alert_P(BEEPS, LENGTH, TONE, PSTR(MSG_AIRGATE_50_0), PSTR(""), false); *fired = true; @@ -43,11 +37,12 @@ /** * Reminds to set the air gate to 25% when the fire is burning down and the - * temperature went below 800°C. + * temperature went below TEMP_AIRGATE_25. */ static void airgate25(bool* const fired, int8_t const dir, Measurement const meas) { - if (! *fired && dir == burning_down && meas.tempI < 800) { + if (! *fired && dir == burning_down && + meas.tempI < TEMP_AIRGATE_25) { airgate = 25; alert_P(BEEPS, LENGTH, TONE, PSTR(MSG_AIRGATE_25_0), PSTR(""), false); *fired = true; @@ -56,11 +51,12 @@ /** * Reminds to close the air gate when the fire is burning down and the - * temperature went below 400°C (no more flames). + * temperature went below TEMP_AIRGATE_0 (no more flames). */ static void airgateClose(bool* const fired, int8_t const dir, Measurement const meas) { - if (! *fired && dir == burning_down && meas.tempI < 450) { + if (! *fired && dir == burning_down && + meas.tempI < TEMP_AIRGATE_0) { setHeaterOn(false); airgate = 0; alert_P(BEEPS, LENGTH, TONE, @@ -74,14 +70,15 @@ */ static void tooRich(bool* const fired, int8_t const dir, Measurement const meas) { - if (! *fired && meas.tempI > 100 && meas.lambda < 1200 && + if (! *fired && meas.tempI > TEMP_FIRE_OUT && + meas.lambda < LAMBDA_TOO_RICH && getHeaterState() == heaterStateReady && airgate < 100) { airgate = 100; alert_P(BEEPS, LENGTH, TONE, PSTR(MSG_TOO_RICH_0), PSTR(MSG_TOO_RICH_1), false); *fired = true; } - if (meas.lambda >= 1300) { + if (meas.lambda >= LAMBDA_TOO_RICH_RESET) { *fired = false; } } @@ -91,28 +88,29 @@ */ static void tooLean(bool* const fired, int8_t const dir, Measurement const meas) { - if (! *fired && meas.tempI > 500 && meas.lambda > 1600 && + if (! *fired && meas.tempI > TEMP_AIRGATE_50 && + meas.lambda > LAMBDA_TOO_LEAN && getHeaterState() == heaterStateReady && airgate > 50) { airgate = 50; alert_P(BEEPS, LENGTH, TONE, PSTR(MSG_AIRGATE_50_0), PSTR(""), false); *fired = true; } - if (meas.lambda <= 1500) { + if (meas.lambda <= LAMBDA_TOO_LEAN_RESET) { *fired = false; } } /** - * Notifies that the fire might have gone out at the beginning of building up. + * Notifies that the fire might have gone out at the beginning of firing up. */ static void fireOut(bool* const fired, int8_t const dir, Measurement const meas) { - if (! *fired && dir == firing_up && meas.tempI < 100 && - rulesMeasMax.tempI - meas.tempI > 25) { + if (! *fired && dir == firing_up && meas.tempI < TEMP_FIRE_OUT && + rulesMeasMax.tempI - meas.tempI > (TEMP_FIRE_OUT_RESET - TEMP_FIRE_OUT)) { alert_P(BEEPS, LENGTH, TONE, PSTR(MSG_FIRE_OUT_0), PSTR(""), false); *fired = true; } - if (meas.tempI >= 125) { + if (meas.tempI >= TEMP_FIRE_OUT_RESET) { *fired = false; } } @@ -157,19 +155,15 @@ */ static void heaterTimeout(bool* const fired, int8_t const dir, Measurement const meas) { - if (isHeaterOn() && getTime() >= 10800 && meas.tempI < 400) { + if (isHeaterOn() && getTime() >= 10800 && meas.tempI < TEMP_AIRGATE_0) { setHeaterOn(false); alert_P(3, 5, TONE, PSTR(MSG_HEATER_OFF_0), PSTR(MSG_HEATER_OFF_1), false); } } -// TODO what if fired up again without reset? -// - Rules are in state fired (no reset) and don't fire again -// - Heating is not switched on again if already switched off - /** - * Rules applied to every nth averaged measurement + * Rules applied to every nth averaged measurement. */ Rule rules[] = { {false, airgate50}, @@ -181,7 +175,7 @@ }; /** - * Heater rules applied to each not averaged measured heater current value + * Heater rules applied to each not averaged measured heater current value. */ Rule heaterRules[] = { {false, heaterReady}, @@ -189,6 +183,9 @@ {false, heaterTimeout} }; +int8_t getDir(void) { + return dir; +} // called about every second void reason(Measurement const meas) { @@ -210,21 +207,34 @@ age++; // init previous measurements with current measurements - if (! measPrevInit) { + if (! prevInit) { rulesMeasPrev = meas; - measPrevInit = true; + prevInit = true; } // try to figure out if the fire is building up or burning down by // comparing current measurements with ones that are 3 minutes old. if (age >= 180) { dir = none; - if ((meas.tempI - rulesMeasPrev.tempI) >= 10 && - rulesMeasMax.tempI < 800 && meas.lambda >= 2000) { + if ((meas.tempI - rulesMeasPrev.tempI) >= TEMP_DELTA_UP && + rulesMeasMax.tempI < TEMP_MAX && meas.lambda >= LAMBDA_MAX) { dir = firing_up; - } else if ((rulesMeasPrev.tempI - meas.tempI) >= 1 && - rulesMeasMax.tempI > 800 && meas.lambda >= 2000) { + } else if (meas.tempI > TEMP_AIRGATE_0 && meas.lambda < LAMBDA_MAX) { + dir = burning; + } else if ((rulesMeasPrev.tempI - meas.tempI) >= TEMP_DELTA_DOWN && + rulesMeasMax.tempI > TEMP_MAX && meas.lambda >= LAMBDA_MAX) { dir = burning_down; + } else if ((meas.tempI - rulesMeasPrev.tempI) >= TEMP_DELTA_UP) { + // it seems wood has been added - reset some measurements and rules + // dir = warm_start; + rulesMeasMax.tempI = meas.tempI; + size_t rulesSize = sizeof(rules) / sizeof(rules[0]); + for (size_t i = 0; i < rulesSize; i++) { + rules[i].fired = false; + } + if (! isHeaterOn()) { + setHeaterOn(true); + } } rulesMeasPrev = meas; @@ -235,7 +245,7 @@ } void resetRules(void) { - measPrevInit = false; + prevInit = false; rulesMeasMax.tempI = 0; age = 0; diff --git a/lambda/rules.h b/lambda/rules.h index 0df4ed5..bf26c43 100644 --- a/lambda/rules.h +++ b/lambda/rules.h @@ -11,9 +11,28 @@ #include #include "sensors.h" +#define BEEPS 30 +#define LENGTH 10 +#define TONE 31 + +#define TEMP_MAX 800 +#define TEMP_DELTA_UP 10 +#define TEMP_DELTA_DOWN 1 +#define TEMP_AIRGATE_50 500 +#define TEMP_AIRGATE_25 800 +#define TEMP_AIRGATE_0 450 +#define TEMP_FIRE_OUT 100 +#define TEMP_FIRE_OUT_RESET 125 +#define LAMBDA_MAX 2000 +#define LAMBDA_TOO_RICH 1200 +#define LAMBDA_TOO_RICH_RESET 1300 +#define LAMBDA_TOO_LEAN 1600 +#define LAMBDA_TOO_LEAN_RESET 1500 + typedef enum { none = 0, firing_up = 1, + burning = 2, burning_down = -1 } FireDir; diff --git a/lambda/sensors.c b/lambda/sensors.c index d87240c..a37c0c9 100644 --- a/lambda/sensors.c +++ b/lambda/sensors.c @@ -96,7 +96,7 @@ lambdaVoltageAvg = lambdaVoltage + lambdaVoltageAvg - ((lambdaVoltageAvg - 4) >> 3); } else { - lambdaVoltageAvg = 44 << 3; + lambdaVoltageAvg = 44 << 3; // Lambda 2.00 } uint16_t heaterVoltage = linADC(getVoltage(ADC_HEATER));