diff --git a/lambda/adc.c b/lambda/adc.c index bfb56d4..526732a 100644 --- a/lambda/adc.c +++ b/lambda/adc.c @@ -38,7 +38,7 @@ sleep_mode(); overValue += ADC; } - int16_t mV = (((overValue >> 2) * AREF_MV) >> 12) + ADC_OFFSET_MV; + int16_t mV = (((overValue >> 2) * AREF_MV) >> 12); return mV; } diff --git a/lambda/adc.h b/lambda/adc.h index f0eb88b..15fb225 100644 --- a/lambda/adc.h +++ b/lambda/adc.h @@ -12,10 +12,9 @@ #ifndef ADC_H_ #define ADC_H_ -// TODO put in Makefile #define AREF_MV 5000 -#define ADC_OFFSET_MV 7 -#define TEMPO_OP_OFFSET_MV 454 +#define ADC_NONLIN_0 -8 +#define ADC_NONLIN_AREF 12 /** * Sets up reference voltage and clock prescaler of the ADC and enables it. @@ -24,8 +23,7 @@ /** * Returns the voltage sampled at the given ADC input pin doing - * 16x oversampling and taking in account the calibrated AREF and - * ADC offset voltages. + * 16x oversampling and taking in account the calibrated AREF voltage. */ uint16_t getVoltage(uint8_t pin); diff --git a/lambda/command.c b/lambda/command.c index 02c7ffa..fcd9d56 100644 --- a/lambda/command.c +++ b/lambda/command.c @@ -98,7 +98,8 @@ } else if (simulation) { Measurement meas = readMeas(fields, fieldCount); - if (getHeatingState() == HEATING_READY) { + if (getHeatingState() == HEATING_OFF || + getHeatingState() == HEATING_READY) { updateMeas(meas); } reason(meas); diff --git a/lambda/lambda.c b/lambda/lambda.c index f10f0e6..6c088e1 100644 --- a/lambda/lambda.c +++ b/lambda/lambda.c @@ -63,7 +63,8 @@ if (isLogging()) { logMeas(meas); } - if (getHeatingState() == HEATING_READY) { + if (getHeatingState() == HEATING_OFF || + getHeatingState() == HEATING_READY) { updateMeas(meas); } reason(meas); diff --git a/lambda/rules.c b/lambda/rules.c index 85a5d19..a370e14 100644 --- a/lambda/rules.c +++ b/lambda/rules.c @@ -96,6 +96,9 @@ } } +/** + * Notifies that the heating is ready and sets the corresponding state. + */ static void heatingReady(bool* const fired, int8_t const dir, Measurement const meas) { if (! isHeatingOn() || getHeatingState() == HEATING_READY) { @@ -108,6 +111,10 @@ } } +/** + * Notifies that the heating or its connection is faulty and sets the + * corresponding state. + */ static void heatingFault(bool* const fired, int8_t const dir, Measurement const meas) { if (! isHeatingOn() || getHeatingState() == HEATING_FAULT) { @@ -124,11 +131,23 @@ } /** - * Array of rules. + * Switches the heating off if it is still on after 3 hours and there does + * not seem to be a fire. + */ +static void heatingTimeout(bool* const fired, int8_t const dir, + Measurement const meas) { + if (isHeatingOn() && getTime() > SECOND * 10800UL && meas.tempI < 400) { + setHeatingOn(false); + } +} + +// TODO what if fired up again without reset? +// TODO what if reset during burndown? + +/** + * Rules applied to every nth averaged measurement */ Rule rules[] = { - {false, heatingReady}, - {false, heatingFault}, {false, airgate50}, {false, airgate25}, {false, airgateClose}, @@ -136,17 +155,32 @@ {false, fireOut} }; +/** + * Heater rules applied to each not averaged measured heater current value + */ +Rule heaterRules[] = { + {false, heatingReady}, + {false, heatingFault}, + {false, heatingTimeout} +}; + + // called about every second void reason(Measurement const meas) { - // TODO apply only heating* (not averaged) to every measurement - // and the other (averaged) rules only to every 10th measurement? - // if (age % 10 == 0) { + // rules applied to every 10th measurement + if (age % 10 == 0) { size_t rulesSize = sizeof(rules) / sizeof(rules[0]); for (size_t i = 0; i < rulesSize; i++) { rules[i].cond(&(rules[i].fired), dir, meas); } - // } + } + + // rules applied to each measurement + size_t heaterRulesSize = sizeof(heaterRules) / sizeof(heaterRules[0]); + for (size_t i = 0; i < heaterRulesSize; i++) { + heaterRules[i].cond(&(heaterRules[i].fired), dir, meas); + } age++; @@ -184,4 +218,9 @@ for (size_t i = 0; i < rulesSize; i++) { rules[i].fired = false; } + + size_t heaterRulesSize = sizeof(heaterRules) / sizeof(heaterRules[0]); + for (size_t i = 0; i < heaterRulesSize; i++) { + heaterRules[i].fired = false; + } } diff --git a/lambda/sensors.c b/lambda/sensors.c index ebaa14c..ed608d2 100644 --- a/lambda/sensors.c +++ b/lambda/sensors.c @@ -64,6 +64,14 @@ }; /** + * Table used to look up ADC measurement deviation due to non-linearity. + */ +static TableEntry const linADCTable[] = { + { 0, ADC_NONLIN_0 }, + { AREF_MV, ADC_NONLIN_AREF } +}; + +/** * Variables holding averaged voltages*8. */ static uint32_t lambdaVoltageAvg = 44 << 3; // Lambda 2.00 @@ -75,23 +83,25 @@ * calculates an exponential moving average and displays the translated values. */ Measurement measure(void) { - uint32_t tempIVoltage = getVoltage(ADC_TEMPI); + uint32_t tempIVoltage = linADC(getVoltage(ADC_TEMPI)); tempIVoltageAvg = tempIVoltage + tempIVoltageAvg - ((tempIVoltageAvg - 4) >> 3); - uint32_t tempOVoltage = getVoltage(ADC_TEMPO); + uint32_t tempOVoltage = linADC(getVoltage(ADC_TEMPO)); tempOVoltageAvg = tempOVoltage + tempOVoltageAvg - ((tempOVoltageAvg - 4) >> 3); - uint32_t lambdaVoltage = getVoltage(ADC_LAMBDA); + uint32_t lambdaVoltage = linADC(getVoltage(ADC_LAMBDA)); lambdaVoltageAvg = lambdaVoltage + lambdaVoltageAvg - ((lambdaVoltageAvg - 4) >> 3); + uint16_t heatingVoltage = linADC(getVoltage(ADC_HEATING)); + Measurement meas; meas.tempI = toTempI(tempIVoltageAvg >> 3); meas.tempO = toTempO(tempOVoltageAvg >> 3); meas.lambda = toLambda(lambdaVoltageAvg >> 3); - meas.current = toCurrent(getVoltage(ADC_HEATING)); + meas.current = toCurrent(heatingVoltage); return meas; } @@ -158,6 +168,13 @@ return value; } +int16_t linADC(uint16_t const mV) { + size_t size = sizeof(linADCTable) / sizeof(linADCTable[0]); + int16_t dev = lookupLinInter(mV, linADCTable, size); + + return (int16_t)mV - dev; +} + char* toInfo(uint16_t const lambda) { if (lambda > 190) { return MSG_LEAN; diff --git a/lambda/sensors.h b/lambda/sensors.h index c3c497c..74ce199 100644 --- a/lambda/sensors.h +++ b/lambda/sensors.h @@ -93,6 +93,11 @@ int16_t lookupLinInter(uint16_t mV, TableEntry const table[], size_t length); /** + * Returns the given ADC measurement with compensated ADC non-linearity. + */ +int16_t linADC(uint16_t mV); + +/** * Returns a descriptive term such as "Lean" for the given lambda value x1000. * For a wood fire, residual oxygen between 5% and 7% (lambda 1.3 and 1.5) is * a good value, below is rich and above is lean.