diff --git a/lambda-test/Makefile b/lambda-test/Makefile index 7e0e6c8..7f1da67 100644 --- a/lambda-test/Makefile +++ b/lambda-test/Makefile @@ -21,7 +21,7 @@ ## Here you can link to one more directory (and multiple .c files) EXTRA_SOURCE_DIR = ../lambda/ EXTRA_SOURCE_FILES = interrupts.c adc.c sensors.c integers.c \ -lcdroutines.c display.c alert.c command.c strings.c usart.c +lcdroutines.c display.c alert.c command.c strings.c usart.c rules.c ##########------------------------------------------------------########## ########## Programmer Defaults ########## @@ -31,7 +31,7 @@ PROGRAMMER_TYPE = avrisp # extra arguments to avrdude: baud rate, chip type, -F flag, etc. -PROGRAMMER_ARGS = -b 19200 -P /dev/ttyACM3 +PROGRAMMER_ARGS = -b 19200 -P /dev/ttyACM0 ##########------------------------------------------------------########## ########## Makefile Magic! ########## diff --git a/lambda-test/avrjunit.c b/lambda-test/avrjunit.c index 6215c49..4253c8e 100644 --- a/lambda-test/avrjunit.c +++ b/lambda-test/avrjunit.c @@ -34,9 +34,10 @@ char cbuf[24]; char nbuf[64]; char tcbuf[128]; - // TODO use strncat_P (macro?) - strcpy_P(cbuf, (PGM_P)pgm_read_word(&(class.tests[i].class))); - strcpy_P(nbuf, (PGM_P)pgm_read_word(&(class.tests[i].name))); + strncpy_P(cbuf, (PGM_P)pgm_read_word(&(class.tests[i].class)), + sizeof(cbuf)); + strncpy_P(nbuf, (PGM_P)pgm_read_word(&(class.tests[i].name)), + sizeof(nbuf)); snprintf(tcbuf, sizeof(tcbuf), "\n", cbuf, nbuf); printString(tcbuf); diff --git a/lambda-test/display-test.c b/lambda-test/display-test.c index 3e2331a..757d6fd 100644 --- a/lambda-test/display-test.c +++ b/lambda-test/display-test.c @@ -101,7 +101,7 @@ updatePending = false; - resetMeas(); + resetDisplay(); // reset max measurements assertTrue(measMax.tempI == 0); assertTrue(measMax.tempO == 0); diff --git a/lambda-test/sensors-test.c b/lambda-test/sensors-test.c index ac878df..2df4ae3 100644 --- a/lambda-test/sensors-test.c +++ b/lambda-test/sensors-test.c @@ -15,6 +15,7 @@ #include "adc.h" #include "sensors.h" #include "pins.h" +#include "messages.h" static TableEntry const testTable[] = { {10, 10}, @@ -127,21 +128,21 @@ bool testToInfoLean(void) { char* info = toInfo(191); - return ! strcmp(info, LEAN); + return ! strcmp(info, MSG_LEAN); } bool testToInfoOkay(void) { - assertTrue(0 == strcmp(toInfo(190), OKAY)); - assertTrue(0 == strcmp(toInfo(170), OKAY)); - assertTrue(0 == strcmp(toInfo(151), OKAY)); + assertTrue(0 == strcmp(toInfo(190), MSG_OKAY)); + assertTrue(0 == strcmp(toInfo(170), MSG_OKAY)); + assertTrue(0 == strcmp(toInfo(151), MSG_OKAY)); return true; } bool testToInfoIdeal(void) { - assertTrue(0 == strcmp(toInfo(150), IDEAL)); - assertTrue(0 == strcmp(toInfo(140), IDEAL)); - assertTrue(0 == strcmp(toInfo(130), IDEAL)); + assertTrue(0 == strcmp(toInfo(150), MSG_IDEAL)); + assertTrue(0 == strcmp(toInfo(140), MSG_IDEAL)); + assertTrue(0 == strcmp(toInfo(130), MSG_IDEAL)); return true; } @@ -149,7 +150,7 @@ bool testToInfoRich(void) { char* info = toInfo(129); - return ! strcmp(info, RICH); + return ! strcmp(info, MSG_RICH); } /* Test "class" */ diff --git a/lambda/Makefile b/lambda/Makefile index 0394c6b..b34ace8 100644 --- a/lambda/Makefile +++ b/lambda/Makefile @@ -15,7 +15,7 @@ ## If you've split your program into multiple .c / .h files, ## include the additional source (in same directory) here LOCAL_SOURCE = usart.c interrupts.c adc.c sensors.c integers.c lcdroutines.c \ -display.c alert.c command.c strings.c +display.c alert.c command.c strings.c rules.c ## Here you can link to one more directory (and multiple .c files) EXTRA_SOURCE_DIR = @@ -29,7 +29,7 @@ PROGRAMMER_TYPE = avrisp # extra arguments to avrdude: baud rate, chip type, -F flag, etc. -PROGRAMMER_ARGS = -b 19200 -P /dev/ttyACM3 +PROGRAMMER_ARGS = -b 19200 -P /dev/ttyACM0 ##########------------------------------------------------------########## ########## Makefile Magic! ########## diff --git a/lambda/TODO b/lambda/TODO index 54e5991..a5a43b3 100644 --- a/lambda/TODO +++ b/lambda/TODO @@ -6,7 +6,7 @@ - What happens if USART data is sent to the AVR too fast (GtkTerm Send Raw File) * Functionality -- Add rules and alerts according to FIRE RULEZ! +- Implement i18n * Circuit - Look at oxygen sensor voltage with voltmeter/oscilloscope - is the voltage diff --git a/lambda/alert.c b/lambda/alert.c index 1b7e7f6..f205339 100644 --- a/lambda/alert.c +++ b/lambda/alert.c @@ -8,8 +8,6 @@ * */ -#include -#include #include #include "integers.h" #include "alert.h" @@ -51,7 +49,7 @@ } void alert(uint8_t const beeps, uint8_t const length, uint16_t const tone, - char* const line0, char* const line1) { + const char* const line0, const char* const line1) { OCR1A = tone; if (TCNT1 >= tone) TCNT1 = 0; alertActive = true; @@ -61,6 +59,16 @@ displayText(line0, line1); } +void alert_P(uint8_t const beeps, uint8_t const length, uint16_t const tone, + PGM_P const line0_P, PGM_P const line1_P) { + char line0[17]; + char line1[17]; + strncpy_P(line0, line0_P, sizeof(line0)); + strncpy_P(line1, line1_P, sizeof(line1)); + + alert(beeps, length, tone, line0, line1); +} + void cancelAlert(void) { beepCount = 0; oscCount = 0; diff --git a/lambda/alert.h b/lambda/alert.h index 0bc8b61..20b4e95 100644 --- a/lambda/alert.h +++ b/lambda/alert.h @@ -11,6 +11,10 @@ #ifndef ALERT_H_ #define ALERT_H_ +#include +#include +#include + void oscillateBeep(void); /** @@ -25,7 +29,14 @@ * respectively. */ void alert(uint8_t beeps, uint8_t length, uint16_t tone, - char* line0, char* line1); + const char* line0, const char* line1); + +/** + * Like alert(), but line0_P and line1_P are expected to be static pointers to + * strings stored in program space like so: PSTR("string"). + */ +void alert_P(uint8_t beeps, uint8_t length, uint16_t tone, + PGM_P line0_P, PGM_P line1_P); /** * Stops beeping and blocking display updates. diff --git a/lambda/command.c b/lambda/command.c index d463ee4..5aee933 100644 --- a/lambda/command.c +++ b/lambda/command.c @@ -19,6 +19,7 @@ #include "alert.h" #include "command.h" #include "strings.h" +#include "rules.h" static bool simulation = false; static bool logging = false; @@ -37,13 +38,15 @@ split(data, " ", fields, fieldCount); if (strcmp_P(fields[0], PSTR("se")) == 0) { // simulation enable - resetMeas(); + resetDisplay(); + resetRules(); simulation = true; beep(1, 2, 31); } else if (strcmp_P(fields[0], PSTR("sd")) == 0) { // simulation disable - resetMeas(); + resetDisplay(); + resetRules(); simulation = false; beep(1, 2, 31); } @@ -82,6 +85,7 @@ else if (simulation) { Measurement meas = readMeas(fields, fieldCount); updateMeas(meas); + reason(meas); } } diff --git a/lambda/display.c b/lambda/display.c index b371378..26a3db4 100644 --- a/lambda/display.c +++ b/lambda/display.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include "usart.h" @@ -21,12 +22,26 @@ #define MENU_OFF 0 #define MENU_MAX_VALUES 1 +#define MENU_LAST_TEXT 2 uint8_t position = MENU_OFF; bool updatePending = false; Measurement measLatest = {0, 0, 0}; Measurement measMax = {0, 0, 2000}; +static char lastLine0[17]; +static char lastLine1[17]; + +/** + * Sets the given two lines of text on the display. + */ +static void setText(const char* const line0, const char* const line1) { + lcd_setcursor(0, 1); + lcd_string(line0); + lcd_setcursor(0, 2); + lcd_string(line1); +} + /** * Formats the given measurement values and displays them on an 16x2 LCD along * with the given hint. @@ -40,10 +55,7 @@ snprintf(line0, sizeof(line0), "Ti %3dC To %3dC ", meas.tempI, meas.tempO); snprintf(line1, sizeof(line1), "L %d.%02d %s %s", lambdaT.quot, abs(lambdaT.rem), toInfo(lambdax100), hint); - lcd_setcursor(0, 1); - lcd_string(line0); - lcd_setcursor(0, 2); - lcd_string(line1); + setText(line0, line1); } void cycleDisplay(void) { @@ -54,9 +66,14 @@ return; } position++; - if (position > MENU_MAX_VALUES) { + if (position > MENU_LAST_TEXT || + (position == MENU_LAST_TEXT && + strlen(lastLine0) == 0 && strlen(lastLine1) == 0)) { position = MENU_OFF; } + if (position == MENU_LAST_TEXT) { + lcd_clear(); + } beep(1, 2, 31); } @@ -70,7 +87,7 @@ updatePending = true; } -void resetMeas(void) { +void resetDisplay(void) { measMax.tempI = 0; measMax.tempO = 0; measMax.lambda = 2000; @@ -84,6 +101,8 @@ if (position == MENU_MAX_VALUES) { displayMeas(measMax, "|>"); + } else if (position == MENU_LAST_TEXT) { + setText(lastLine0, lastLine1); } else { displayMeas(measLatest, " "); } @@ -98,10 +117,11 @@ printString(log); } -void displayText(char* const line0, char* const line1) { +void displayText(const char* const line0, const char* const line1) { + lastLine0[0] = '\0'; + lastLine1[0] = '\0'; + strncat(lastLine0, line0, sizeof(lastLine0) - 1); + strncat(lastLine1, line1, sizeof(lastLine1) - 1); lcd_clear(); - lcd_setcursor(0, 1); - lcd_string(line0); - lcd_setcursor(0, 2); - lcd_string(line1); + setText(line0, line1); } diff --git a/lambda/display.h b/lambda/display.h index 969c395..0d1ff09 100644 --- a/lambda/display.h +++ b/lambda/display.h @@ -29,7 +29,7 @@ /** * Resets max measurements to initial values and flags pending display update. */ -void resetMeas(void); +void resetDisplay(void); /** * Updates the display if an update is pending. @@ -44,6 +44,6 @@ /** * Displays the given two lines of text. */ -void displayText(char* line0, char* line1); +void displayText(const char* line0, const char* line1); #endif /* DISPLAY_H_ */ diff --git a/lambda/lambda.c b/lambda/lambda.c index 5cac9de..f5badd6 100644 --- a/lambda/lambda.c +++ b/lambda/lambda.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "usart.h" #include "lcdroutines.h" #include "adc.h" @@ -30,6 +31,8 @@ #include "display.h" #include "alert.h" #include "command.h" +#include "rules.h" +#include "messages.h" /** * Does initialization, measures, displays and logs the measurements and @@ -44,7 +47,7 @@ initInterrupts(); initTimers(); - alert(1, 2, 31, " Hello! ", ""); + alert_P(1, 2, 31, PSTR(MSG_WELCOME), PSTR("")); Measurement meas; @@ -56,6 +59,7 @@ logMeas(meas); } updateMeas(meas); + reason(meas); } if (isUSARTReceived()) { char data[64]; diff --git a/lambda/messages.h b/lambda/messages.h new file mode 100644 index 0000000..d11056d --- /dev/null +++ b/lambda/messages.h @@ -0,0 +1,29 @@ +/* + * messages.h + * + * Created on: 24.05.2015 + * Author: dode@luniks.net + */ + +#ifndef MESSAGES_H_ +#define MESSAGES_H_ + +/* lambda.c */ +#define MSG_WELCOME " Hello! " + +/* sensors.c */ +#define MSG_LEAN "Lean " +#define MSG_OKAY "Okay " +#define MSG_IDEAL "Ideal" +#define MSG_RICH "Rich!" + +/* rules.c */ +#define MSG_AIRGATE_50_0 "Air gate 50%" +#define MSG_AIRGATE_25_0 "Air gate 25%" +#define MSG_AIRGATE_CLOSE_0 "Close air gate" +#define MSG_AIRGATE_CLOSE_1 "Turn off heating" +#define MSG_TOO_RICH_0 "Too rich, open" +#define MSG_TOO_RICH_1 "air gate!" +#define MSG_FIRE_OUT_0 "Fire out?" + +#endif /* MESSAGES_H_ */ diff --git a/lambda/rules.c b/lambda/rules.c new file mode 100644 index 0000000..03b6956 --- /dev/null +++ b/lambda/rules.c @@ -0,0 +1,156 @@ +/* + * rules.c + * + * Created on: 22.05.2015 + * Author: dode@luniks.net + */ + +#include +#include "alert.h" +#include "integers.h" +#include "rules.h" +#include "messages.h" + +#define BEEPS 60 +#define LENGTH 20 +#define TONE 31 + +#define DIR_NONE 0 +#define DIR_BURN_UP 1 +#define DIR_BURN_DOWN -1 + +Measurement rulesMeasMax = {0, 0, 2000}; +Measurement rulesMeasPrev = {0, 0, 2000}; +uint16_t age = 0; +int8_t dir = 0; + +/** + * Reminds to set the air gate to 50% when the fire is still building up + * and the temperature has reached 500°C. + */ +static void airgate50(bool* const fired, int8_t const dir, + Measurement const meas) { + if (! *fired && dir == DIR_BURN_UP && meas.tempI >= 500) { + alert_P(BEEPS, LENGTH, TONE, PSTR(MSG_AIRGATE_50_0), PSTR("")); + *fired = true; + } +} + +/** + * Reminds to set the air gate to 25% when the fire is burning down and the + * temperature went below 800°C. + */ +static void airgate25(bool* const fired, int8_t const dir, + Measurement const meas) { + if (! *fired && dir == DIR_BURN_DOWN && meas.tempI < 800) { + alert_P(BEEPS, LENGTH, TONE, PSTR(MSG_AIRGATE_50_0), PSTR("")); + *fired = true; + } +} + +/** + * Reminds to close the air gate when the fire is burning down and the + * temperature went below 400°C. + */ +static void airgateClose(bool* const fired, int8_t const dir, + Measurement const meas) { + if (! *fired && dir == DIR_BURN_DOWN && meas.tempI < 400) { + alert_P(BEEPS, LENGTH, TONE, + PSTR(MSG_AIRGATE_CLOSE_0), PSTR(MSG_AIRGATE_CLOSE_1)); + *fired = true; + } +} + +/** + * Notifies that the combustion is too rich and to open the air gate + * if possible. + */ +static void tooRich(bool* const fired, int8_t const dir, + Measurement const meas) { + if (! *fired && meas.tempI >= 100 && meas.lambda < 1200) { + alert_P(BEEPS, LENGTH, TONE, + PSTR(MSG_TOO_RICH_0), PSTR(MSG_TOO_RICH_1)); + *fired = true; + } + if (meas.lambda >= 1300) { + *fired = false; + } +} + +/** + * Notifies that the might have gone out at the beginning of building up. + */ +static void fireOut(bool* const fired, int8_t const dir, + Measurement const meas) { + if (! *fired && dir == DIR_BURN_UP && meas.tempI < 100 && + meas.tempI < rulesMeasMax.tempI) { + alert_P(BEEPS, LENGTH, TONE, PSTR(MSG_FIRE_OUT_0), PSTR("")); + *fired = true; + } + if (meas.tempI >= 100) { + *fired = false; + } +} + +/** + * Array of rules. + */ +Rule rules[] = { + {false, airgate50}, + {false, airgate25}, + {false, airgateClose}, + {false, tooRich}, + {false, fireOut} +}; + +// called about every second +void reason(Measurement const meas) { + + // apply the rules about every 10 seconds + 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); + } + } + + // 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 = DIR_NONE; + if ((meas.tempI - rulesMeasPrev.tempI) >= 10 && + rulesMeasMax.tempI < 800 && meas.lambda >= 2000) { + dir = DIR_BURN_UP; + } else if ((rulesMeasPrev.tempI - meas.tempI) >= 1 && + rulesMeasMax.tempI > 800 && meas.lambda >= 2000) { + dir = DIR_BURN_DOWN; + } + + rulesMeasPrev = meas; + age = 0; + } + + rulesMeasMax.tempI = MAX(rulesMeasMax.tempI, meas.tempI); + rulesMeasMax.tempO = MAX(rulesMeasMax.tempO, meas.tempO); + rulesMeasMax.lambda = MIN(rulesMeasMax.lambda, meas.lambda); + + age++; +} + +void resetRules(void) { + rulesMeasPrev.tempI = 0; + rulesMeasPrev.tempO = 0; + rulesMeasPrev.lambda = 2000; + + rulesMeasMax.tempI = 0; + rulesMeasMax.tempO = 0; + rulesMeasMax.lambda = 2000; + + age = 0; + dir = DIR_NONE; + + size_t rulesSize = sizeof(rules) / sizeof(rules[0]); + for (size_t i = 0; i < rulesSize; i++) { + rules[i].fired = false; + } +} diff --git a/lambda/rules.h b/lambda/rules.h new file mode 100644 index 0000000..660deb5 --- /dev/null +++ b/lambda/rules.h @@ -0,0 +1,32 @@ +/* + * rules.h + * + * Created on: 22.05.2015 + * Author: dode@luniks.net + */ + +#ifndef RULES_H_ +#define RULES_H_ + +#include +#include "sensors.h" + +/** + * An attempt to create some sort of rule "object". + */ +typedef struct { + bool fired; + void (*cond)(bool* fired, int8_t dir, Measurement meas); +} Rule; + +/** + * Applies all rules against the given measurements. + */ +void reason(Measurement meas); + +/** + * Resets all rules and internal state. + */ +void resetRules(void); + +#endif /* RULES_H_ */ diff --git a/lambda/sensors.c b/lambda/sensors.c index 5b8fadf..fd4b18b 100644 --- a/lambda/sensors.c +++ b/lambda/sensors.c @@ -8,7 +8,6 @@ * */ -#include #include #include #include @@ -16,6 +15,7 @@ #include "sensors.h" #include "integers.h" #include "pins.h" +#include "messages.h" /** * Table used to look up the lambda value at 12 V heater voltage @@ -146,12 +146,12 @@ char* toInfo(uint16_t const lambda) { if (lambda > 190) { - return LEAN; + return MSG_LEAN; } else if (lambda > 150 && lambda <= 190) { - return OKAY; + return MSG_OKAY; } else if (lambda >= 130 && lambda <= 150) { - return IDEAL; + return MSG_IDEAL; } else { - return RICH; + return MSG_RICH; } } diff --git a/lambda/sensors.h b/lambda/sensors.h index 3b13514..31f229b 100644 --- a/lambda/sensors.h +++ b/lambda/sensors.h @@ -11,11 +11,7 @@ #ifndef SENSORS_H_ #define SENSORS_H_ -// TODO put in Makefile? -#define LEAN "Mager" -#define OKAY "Okay " -#define IDEAL "Ideal" -#define RICH "Fett!" +#include /** * Entry for the lookup tables. diff --git a/lambda/usart.c b/lambda/usart.c index aaae4c5..0fee17c 100644 --- a/lambda/usart.c +++ b/lambda/usart.c @@ -53,9 +53,9 @@ if (size > 0) { data[0] = '\0'; strncat(data, usartData, size - 1); + memset(usartData, 0, sizeof(usartData)); + usartReceived = false; } - memset(usartData, 0, sizeof(usartData)); - usartReceived = false; } void printString(char* const data) {