diff --git a/lambda-test/avrjunit.c b/lambda-test/avrjunit.c index 451e833..e25a256 100644 --- a/lambda-test/avrjunit.c +++ b/lambda-test/avrjunit.c @@ -14,7 +14,7 @@ #include "USART.h" #include "avrjunit.h" -void runTests(char* const suite, TestCase const tests[], uint16_t const count) { +void runTests(char* const suite, TestCase const tests[], size_t const count) { printString("\n"); char tsbuf[128]; snprintf(tsbuf, sizeof(tsbuf), diff --git a/lambda-test/avrjunit.h b/lambda-test/avrjunit.h index 01a4dac..2b49867 100644 --- a/lambda-test/avrjunit.h +++ b/lambda-test/avrjunit.h @@ -34,7 +34,7 @@ * A test case with its class, name and test function pointer, * which should return true on success and false on failure. * The strings class and name are expected to be stored in program space - * like so: const char class[] PROGMEM = "class"; + * like so: const char testName[] PROGMEM = "testName"; */ typedef struct PROGMEM { PGM_P class; diff --git a/lambda-test/lambda-test.c b/lambda-test/lambda-test.c index 3e57052..b795838 100644 --- a/lambda-test/lambda-test.c +++ b/lambda-test/lambda-test.c @@ -28,6 +28,7 @@ #include "avrjunit.h" #include "interrupts.h" #include "adc.h" +#include "alert.h" #include "integers.h" #include "sensors.h" #include "display.h" @@ -98,13 +99,129 @@ /* Module display */ bool testCycle(void) { - // extern uint8_t position; + extern uint8_t position; + extern bool updatePending; - assertTrue(getPosition() == 0); + extern uint8_t beepCount; + extern uint16_t beepLength; + + updatePending = false; + + assertTrue(position == 0); + assertFalse(updatePending); + cycleDisplay(); - assertTrue(getPosition() == 1); + assertTrue(position == 1); + assertTrue(updatePending); + assertTrue(beepCount == 1); + assertTrue(beepLength == 2); + cycleDisplay(); - assertTrue(getPosition() == 0); + assertTrue(position == 0); + + return true; +} + +bool testCycleCancelAlert(void) { + extern uint8_t position; + extern bool updatePending; + + updatePending = false; + + alert(1, 1, "", ""); + assertTrue(isAlertActive()); + + cycleDisplay(); + assertFalse(isAlertActive()); + assertTrue(position == 0); + assertTrue(updatePending); + + return true; +} + +bool testUpdateMeas(void) { + extern bool updatePending; + extern Measurement measLatest; + extern Measurement measMax; // = {0, 0, 2000}; + + updatePending = false; + + // initial measurements + assertTrue(measLatest.tempI == 0); + assertTrue(measLatest.tempO == 0); + assertTrue(measLatest.lambda == 0); + // initial max measurements + assertTrue(measMax.tempI == 0); + assertTrue(measMax.tempO == 0); + assertTrue(measMax.lambda == 2000); + + Measurement meas1 = {1, 2, 3}; + updateMeas(meas1); + // updated measurements + assertTrue(measLatest.tempI == 1); + assertTrue(measLatest.tempO == 2); + assertTrue(measLatest.lambda == 3); + // updated max measurements + assertTrue(measMax.tempI == 1); + assertTrue(measMax.tempO == 2); + assertTrue(measMax.lambda == 3); + assertTrue(updatePending); + + Measurement meas2 = {0, 0, 10}; + updateMeas(meas2); + // updated max measurements + assertTrue(measMax.tempI == 1); + assertTrue(measMax.tempO == 2); + assertTrue(measMax.lambda == 3); + + return true; +} + +bool testResetMeas(void) { + extern bool updatePending; + extern Measurement measLatest; + extern Measurement measMax; + + updatePending = false; + + resetMeas(); + // reset max measurements + assertTrue(measMax.tempI == 0); + assertTrue(measMax.tempO == 0); + assertTrue(measMax.lambda == 2000); + assertTrue(updatePending); + + return true; +} + +bool testUpdateDisplayIfPending(void) { + extern bool updatePending; + + updatePending = true; + + updateDisplayIfPending(); + assertFalse(updatePending); + + return true; +} + +bool testUpdateDisplayIfPendingAlertActive(void) { + extern bool updatePending; + + updatePending = true; + alert(1, 1, "", ""); + assertTrue(isAlertActive()); + + // update should be skipped if alert is active + updateDisplayIfPending(); + assertTrue(updatePending); + + return true; +} + +bool testDisplayText(void) { + // won't actually display anything + displayText("testDisplayText", "testDisplayTextLineTooLong"); return true; } @@ -267,7 +384,7 @@ bool testReadMeas(void) { char* fields[] = {"1", "2", "3"}; - Measurement meas = readMeas(fields); + Measurement meas = readMeas(fields, 3); assertTrue(meas.tempI == 1); assertTrue(meas.tempO == 2); assertTrue(meas.lambda == 3); @@ -275,6 +392,17 @@ return true; } +bool testReadMeasTooFewFields(void) { + char* fields[] = {"1"}; + + Measurement meas = readMeas(fields, 1); + assertTrue(meas.tempI == 0); + assertTrue(meas.tempO == 0); + assertTrue(meas.lambda == 0); + + return true; +} + bool testToLambdaValue(void) { int16_t lambda = toLambda(132); @@ -372,10 +500,6 @@ return true; } -/* - * Whoa. Trying to write more elements than its size in the fields array - * seems to cause the AVR to reset and rerun the tests in an infinite loop. - */ bool testSplitSizeTooSmall(void) { char string[] = "f1 f2"; char* fields[1]; @@ -396,83 +520,97 @@ const char strings_P[] PROGMEM = "strings"; /* Test names */ -const char t01_P[] PROGMEM = "testSetupADC"; -const char t02_P[] PROGMEM = "testGetVoltage"; -const char t03_P[] PROGMEM = "testIsSimulation"; -const char t04_P[] PROGMEM = "testIsLogging"; -const char t05_P[] PROGMEM = "testCycle"; -const char t06_P[] PROGMEM = "testDivRoundNearest"; -const char t07_P[] PROGMEM = "testDivRoundNearestNumNeg"; -const char t08_P[] PROGMEM = "testDivRoundNearestDenNeg"; -const char t09_P[] PROGMEM = "testDivRoundNearestBothNeg"; -const char t10_P[] PROGMEM = "testDivRoundUp"; -const char t11_P[] PROGMEM = "testDivRoundUpNumNeg"; -const char t12_P[] PROGMEM = "testDivRoundUpDenNeg"; -const char t13_P[] PROGMEM = "testDivRoundUpBothNeg"; -const char t14_P[] PROGMEM = "testSetupPorts"; -const char t15_P[] PROGMEM = "testSetupSleepMode"; -const char t16_P[] PROGMEM = "testInitInterrupts"; -const char t17_P[] PROGMEM = "testInitTimers"; -const char t18_P[] PROGMEM = "testMeasure"; -const char t19_P[] PROGMEM = "testReadMeas"; -const char t20_P[] PROGMEM = "testToLambdaValue"; -const char t21_P[] PROGMEM = "testToLambdaInter"; -const char t22_P[] PROGMEM = "testToTempI"; -const char t23_P[] PROGMEM = "testToTempOValue"; -const char t24_P[] PROGMEM = "testToTempOInter"; -const char t25_P[] PROGMEM = "testLookupLinInterValue"; -const char t26_P[] PROGMEM = "testLookupLinInterInter"; -const char t27_P[] PROGMEM = "testLookupLinInterBelow"; -const char t28_P[] PROGMEM = "testLookupLinInterAbove"; -const char t29_P[] PROGMEM = "testToInfoLean"; -const char t30_P[] PROGMEM = "testToInfoOkay"; -const char t31_P[] PROGMEM = "testToInfoIdeal"; -const char t32_P[] PROGMEM = "testToInfoRich"; -const char t33_P[] PROGMEM = "testSplit"; -const char t34_P[] PROGMEM = "testSplitSizeTooSmall"; +const char testSetupADC_P[] PROGMEM = "testSetupADC"; +const char testGetVoltage_P[] PROGMEM = "testGetVoltage"; +const char testIsSimulation_P[] PROGMEM = "testIsSimulation"; +const char testIsLogging_P[] PROGMEM = "testIsLogging"; +const char testCycle_P[] PROGMEM = "testCycle"; +const char testCycleCancelAlert_P[] PROGMEM = "testCycleCancelAlert"; +const char testUpdateMeas_P[] PROGMEM = "testUpdateMeas"; +const char testResetMeas_P[] PROGMEM = "testResetMeas"; +const char testUpdateDisplayIfPending_P[] PROGMEM = "testUpdateDisplayIfPending"; +const char testUpdateDisplayIfPendingAlertActive_P[] PROGMEM = "testUpdateDisplayIfPendingAlertActive"; +const char testDisplayText_P[] PROGMEM = "testDisplayText"; +const char testDivRoundNearest_P[] PROGMEM = "testDivRoundNearest"; +const char testDivRoundNearestNumNeg_P[] PROGMEM = "testDivRoundNearestNumNeg"; +const char testDivRoundNearestDenNeg_P[] PROGMEM = "testDivRoundNearestDenNeg"; +const char testDivRoundNearestBothNeg_P[] PROGMEM = "testDivRoundNearestBothNeg"; +const char testDivRoundUp_P[] PROGMEM = "testDivRoundUp"; +const char testDivRoundUpNumNeg_P[] PROGMEM = "testDivRoundUpNumNeg"; +const char testDivRoundUpDenNeg_P[] PROGMEM = "testDivRoundUpDenNeg"; +const char testDivRoundUpBothNeg_P[] PROGMEM = "testDivRoundUpBothNeg"; +const char testSetupPorts_P[] PROGMEM = "testSetupPorts"; +const char testSetupSleepMode_P[] PROGMEM = "testSetupSleepMode"; +const char testInitInterrupts_P[] PROGMEM = "testInitInterrupts"; +const char testInitTimers_P[] PROGMEM = "testInitTimers"; +const char testMeasure_P[] PROGMEM = "testMeasure"; +const char testReadMeas_P[] PROGMEM = "testReadMeas"; +const char testReadMeasTooFewFields_P[] PROGMEM = "testReadMeasTooFewFields"; +const char testToLambdaValue_P[] PROGMEM = "testToLambdaValue"; +const char testToLambdaInter_P[] PROGMEM = "testToLambdaInter"; +const char testToTempI_P[] PROGMEM = "testToTempI"; +const char testToTempOValue_P[] PROGMEM = "testToTempOValue"; +const char testToTempOInter_P[] PROGMEM = "testToTempOInter"; +const char testLookupLinInterValue_P[] PROGMEM = "testLookupLinInterValue"; +const char testLookupLinInterInter_P[] PROGMEM = "testLookupLinInterInter"; +const char testLookupLinInterBelow_P[] PROGMEM = "testLookupLinInterBelow"; +const char testLookupLinInterAbove_P[] PROGMEM = "testLookupLinInterAbove"; +const char testToInfoLean_P[] PROGMEM = "testToInfoLean"; +const char testToInfoOkay_P[] PROGMEM = "testToInfoOkay"; +const char testToInfoIdeal_P[] PROGMEM = "testToInfoIdeal"; +const char testToInfoRich_P[] PROGMEM = "testToInfoRich"; +const char testSplit_P[] PROGMEM = "testSplit"; +const char testSplitSizeTooSmall_P[] PROGMEM = "testSplitSizeTooSmall"; /* Tests */ TestCase const tests[] = { - {adc_P, t01_P, testSetupADC}, - {adc_P, t02_P, testGetVoltage}, - {command_P, t03_P, testIsSimulation}, - {command_P, t04_P, testIsLogging}, - {display_P, t05_P, testCycle}, - {integers_P, t06_P, testDivRoundNearest}, - {integers_P, t07_P, testDivRoundNearestNumNeg}, - {integers_P, t08_P, testDivRoundNearestDenNeg}, - {integers_P, t09_P, testDivRoundNearestBothNeg}, - {integers_P, t10_P, testDivRoundUp}, - {integers_P, t11_P, testDivRoundUpNumNeg}, - {integers_P, t12_P, testDivRoundUpDenNeg}, - {integers_P, t13_P, testDivRoundUpBothNeg}, - {interrupts_P, t14_P, testSetupPorts}, - {interrupts_P, t15_P, testSetupSleepMode}, - {interrupts_P, t16_P, testInitInterrupts}, - {interrupts_P, t17_P, testInitTimers}, - {sensors_P, t18_P, testMeasure}, - {sensors_P, t19_P, testReadMeas}, - {sensors_P, t20_P, testToLambdaValue}, - {sensors_P, t21_P, testToLambdaInter}, - {sensors_P, t22_P, testToTempI}, - {sensors_P, t23_P, testToTempOValue}, - {sensors_P, t24_P, testToTempOInter}, - {sensors_P, t25_P, testLookupLinInterValue}, - {sensors_P, t26_P, testLookupLinInterInter}, - {sensors_P, t27_P, testLookupLinInterBelow}, - {sensors_P, t28_P, testLookupLinInterAbove}, - {sensors_P, t29_P, testToInfoLean}, - {sensors_P, t30_P, testToInfoOkay}, - {sensors_P, t31_P, testToInfoIdeal}, - {sensors_P, t32_P, testToInfoRich}, - {strings_P, t33_P, testSplit}, - {strings_P, t34_P, testSplitSizeTooSmall} + {adc_P, testSetupADC_P, testSetupADC}, + {adc_P, testGetVoltage_P, testGetVoltage}, + {command_P, testIsSimulation_P, testIsSimulation}, + {command_P, testIsLogging_P, testIsLogging}, + {display_P, testCycle_P, testCycle}, + {display_P, testCycleCancelAlert_P, testCycleCancelAlert}, + {display_P, testUpdateMeas_P, testUpdateMeas}, + {display_P, testResetMeas_P, testResetMeas}, + {display_P, testUpdateDisplayIfPending_P, testUpdateDisplayIfPending}, + {display_P, testUpdateDisplayIfPendingAlertActive_P, testUpdateDisplayIfPendingAlertActive}, + {display_P, testDisplayText_P, testDisplayText}, + {integers_P, testDivRoundNearest_P, testDivRoundNearest}, + {integers_P, testDivRoundNearestNumNeg_P, testDivRoundNearestNumNeg}, + {integers_P, testDivRoundNearestDenNeg_P, testDivRoundNearestDenNeg}, + {integers_P, testDivRoundNearestBothNeg_P, testDivRoundNearestBothNeg}, + {integers_P, testDivRoundUp_P, testDivRoundUp}, + {integers_P, testDivRoundUpNumNeg_P, testDivRoundUpNumNeg}, + {integers_P, testDivRoundUpDenNeg_P, testDivRoundUpDenNeg}, + {integers_P, testDivRoundUpBothNeg_P, testDivRoundUpBothNeg}, + {interrupts_P, testSetupPorts_P, testSetupPorts}, + {interrupts_P, testSetupSleepMode_P, testSetupSleepMode}, + {interrupts_P, testInitInterrupts_P, testInitInterrupts}, + {interrupts_P, testInitTimers_P, testInitTimers}, + {sensors_P, testMeasure_P, testMeasure}, + {sensors_P, testReadMeas_P, testReadMeas}, + {sensors_P, testReadMeasTooFewFields_P, testReadMeasTooFewFields}, + {sensors_P, testToLambdaValue_P, testToLambdaValue}, + {sensors_P, testToLambdaInter_P, testToLambdaInter}, + {sensors_P, testToTempI_P, testToTempI}, + {sensors_P, testToTempOValue_P, testToTempOValue}, + {sensors_P, testToTempOInter_P, testToTempOInter}, + {sensors_P, testLookupLinInterValue_P, testLookupLinInterValue}, + {sensors_P, testLookupLinInterInter_P, testLookupLinInterInter}, + {sensors_P, testLookupLinInterBelow_P, testLookupLinInterBelow}, + {sensors_P, testLookupLinInterAbove_P, testLookupLinInterAbove}, + {sensors_P, testToInfoLean_P, testToInfoLean}, + {sensors_P, testToInfoOkay_P, testToInfoOkay}, + {sensors_P, testToInfoIdeal_P, testToInfoIdeal}, + {sensors_P, testToInfoRich_P, testToInfoRich}, + {strings_P, testSplit_P, testSplit}, + {strings_P, testSplitSizeTooSmall_P, testSplitSizeTooSmall} }; int main(void) { initUSART(); - uint16_t count = sizeof(tests) / sizeof(tests[0]); + size_t count = sizeof(tests) / sizeof(tests[0]); runTests("lambda", tests, count); return 0; diff --git a/lambda/.gitignore b/lambda/.gitignore index 0720336..78a44cc 100644 --- a/lambda/.gitignore +++ b/lambda/.gitignore @@ -3,3 +3,4 @@ /lambda.elf /lambda.hex /lambda.lst +/pins.h diff --git a/lambda/alert.c b/lambda/alert.c index 0e2cfd1..87c6565 100644 --- a/lambda/alert.c +++ b/lambda/alert.c @@ -15,9 +15,10 @@ #include "sensors.h" #include "display.h" +uint8_t beepCount = 0; +uint16_t beepLength = 0; + static uint8_t oscCount = 0; -static uint8_t beepCount = 0; -static uint16_t beepLength = 0; static bool alertActive = false; void oscillateBeep(void) { diff --git a/lambda/command.c b/lambda/command.c index 83adb5b..43704c7 100644 --- a/lambda/command.c +++ b/lambda/command.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "sensors.h" #include "display.h" @@ -31,40 +32,43 @@ } void runCommand(char* const data) { - char* fields[8]; - split(data, " ", fields, 8); - if (strcmp(fields[0], "se") == 0) { + size_t fieldCount = 8; + char* fields[fieldCount]; + split(data, " ", fields, fieldCount); + if (strcmp_P(fields[0], PSTR("se")) == 0) { // simulation enable resetMeas(); simulation = true; beep(1, 2); } - else if (strcmp(fields[0], "sd") == 0) { + else if (strcmp_P(fields[0], PSTR("sd")) == 0) { // simulation disable resetMeas(); simulation = false; beep(1, 2); } - else if (strcmp(fields[0], "le") == 0) { + else if (strcmp_P(fields[0], PSTR("le")) == 0) { // logging enable logging = true; beep(1, 2); } - else if (strcmp(fields[0], "ld") == 0) { + else if (strcmp_P(fields[0], PSTR("ld")) == 0) { // logging disable logging = false; beep(1, 2); } - else if (strcmp(fields[0], "cm") == 0) { + else if (strcmp_P(fields[0], PSTR("cm")) == 0) { // cycle menu cycleDisplay(); } - else if (strcmp(fields[0], "ta") == 0) { + else if (strcmp_P(fields[0], PSTR("ta")) == 0) { // test alert - alert(1, 20, "Beep!", fields[1]); + char buf[16]; + strcpy_P(buf, PSTR("Beep Beep Beep!")); + alert(3, 10, buf, fields[1]); } else if (simulation) { - Measurement meas = readMeas(fields); + Measurement meas = readMeas(fields, fieldCount); updateMeas(meas); } } diff --git a/lambda/display.c b/lambda/display.c index 1629234..9ed53a5 100644 --- a/lambda/display.c +++ b/lambda/display.c @@ -23,16 +23,10 @@ #define MENU_OFF 0 #define MENU_MAX_VALUES 1 -static uint8_t position = MENU_OFF; -static bool updatePending = false; -static Measurement measLatest; -static Measurement measMax = {0, 0, 2000}; - -// TODO unused/unnecessary "getter" does not add on program or data memory -// since only called from test, still a good idea? -uint8_t getPosition(void) { - return position; -} +uint8_t position = MENU_OFF; +bool updatePending = false; +Measurement measLatest = {0, 0, 0}; +Measurement measMax = {0, 0, 2000}; /** * Formats the given measurement values and displays them on an 16x2 LCD along diff --git a/lambda/interrupts.c b/lambda/interrupts.c index 9835fc8..29a4efc 100644 --- a/lambda/interrupts.c +++ b/lambda/interrupts.c @@ -43,7 +43,7 @@ ISR(USART_RX_vect) { if (bit_is_set(UCSR0A, RXC0) && ! usartReceived) { char data = UDR0; - uint8_t length = strlen(usartData); + size_t length = strlen(usartData); if (length < sizeof(usartData) - 1 && data != '\n' && data != '\r') { usartData[length] = data; } else { @@ -59,7 +59,7 @@ } // TODO doesn't really belong in this source file -void getUSARTData(char* const data, uint8_t const size) { +void getUSARTData(char* const data, size_t const size) { if (size > 0) { data[0] = '\0'; strncat(data, usartData, size - 1); diff --git a/lambda/interrupts.h b/lambda/interrupts.h index 0e81e80..8cf2f9e 100644 --- a/lambda/interrupts.h +++ b/lambda/interrupts.h @@ -20,7 +20,7 @@ * Appends the data received via USART to the given string with the given * length. */ -void getUSARTData(char* data, uint8_t length); +void getUSARTData(char* data, size_t length); /** * Returns true if the current timer interrupt count is equal or greater to diff --git a/lambda/pins.h b/lambda/pins.h index 328abbd..290ed70 100644 --- a/lambda/pins.h +++ b/lambda/pins.h @@ -13,18 +13,18 @@ /** ADC pin for the type K thermocouple signal */ #define ADC_TEMPI PC5 /** ADC pin for the PT1000 resistance thermometer signal */ -#define ADC_TEMPO PC4 // prototype: PC0 +#define ADC_TEMPO PC0 // prototype: PC0 /** ADC pin for the LSM 11 oxygen sensor signal */ -#define ADC_LAMBDA PC3 // prototype: PC2 +#define ADC_LAMBDA PC2 // prototype: PC2 /* Pins for the LCD */ #define LCD_PORT PORTD #define LCD_DDR DDRD -#define LCD_RS PD7 // prototype: PD6 -#define LCD_EN PD6 // prototype: PD7 +#define LCD_RS PD6 // prototype: PD6 +#define LCD_EN PD7 // prototype: PD7 #define LCD_DB4 PD5 -#define LCD_DB5 PD4 // prototype: PD2 +#define LCD_DB5 PD2 // prototype: PD2 #define LCD_DB6 PD3 -#define LCD_DB7 PD2 // prototype: PD4 +#define LCD_DB7 PD4 // prototype: PD4 #endif /* PINS_H_ */ diff --git a/lambda/sensors.c b/lambda/sensors.c index 20c9964..f67f84f 100644 --- a/lambda/sensors.c +++ b/lambda/sensors.c @@ -90,9 +90,11 @@ return meas; } -Measurement readMeas(char* const fields[]) { +Measurement readMeas(char* const fields[], size_t const size) { Measurement meas; - // TODO can check if fields[] has 3 elements? + if (size < 3) { + return meas; + } meas.tempI = atoi(fields[0]); meas.tempO = atoi(fields[1]); meas.lambda = atoi(fields[2]); @@ -107,29 +109,29 @@ } int16_t toTempO(uint16_t const mV) { - uint8_t length = sizeof(tempOTable) / sizeof(tempOTable[0]); - int16_t temp = lookupLinInter(mV, tempOTable, length); + size_t size = sizeof(tempOTable) / sizeof(tempOTable[0]); + int16_t temp = lookupLinInter(mV, tempOTable, size); return temp; } int16_t toLambda(uint16_t const mV) { - uint8_t length = sizeof(lambdaTable) / sizeof(lambdaTable[0]); - int16_t lambda = lookupLinInter(mV, lambdaTable, length); + size_t size = sizeof(lambdaTable) / sizeof(lambdaTable[0]); + int16_t lambda = lookupLinInter(mV, lambdaTable, size); return lambda; } int16_t lookupLinInter(uint16_t const mV, TableEntry const table[], - uint8_t const length) { + size_t const size) { if (mV < table[0].mV) { return table[0].value; - } else if (mV > table[length - 1].mV) { - return table[length - 1].value; + } else if (mV > table[size - 1].mV) { + return table[size - 1].value; } uint8_t i = 0; - for (; i < length - 1; i++) { + for (; i < size - 1; i++) { if (table[i + 1].mV > mV) { break; } diff --git a/lambda/sensors.h b/lambda/sensors.h index 25adcee..3b13514 100644 --- a/lambda/sensors.h +++ b/lambda/sensors.h @@ -45,7 +45,7 @@ * a burnoff with data logged during real burnoffs. The voltages are not used, * the space separated fields are tempI, tempO and lambda. */ -Measurement readMeas(char* const usartData[]); +Measurement readMeas(char* const usartData[], size_t size); /** * Returns the temperature for the given voltage of a type K thermocouple @@ -78,7 +78,7 @@ * Thanks to http://stackoverflow.com/a/7091629/709426 and * http://en.wikipedia.org/wiki/Linear_interpolation */ -int16_t lookupLinInter(uint16_t mV, TableEntry const table[], uint8_t length); +int16_t lookupLinInter(uint16_t mV, TableEntry const table[], size_t length); /** * Returns a descriptive term such as "Lean" for the given lambda value x1000.