diff --git a/software/firmware/tester_runtime/src/leds.c b/software/firmware/tester_runtime/src/leds.c index 06c5ab1..2b525b1 100644 --- a/software/firmware/tester_runtime/src/leds.c +++ b/software/firmware/tester_runtime/src/leds.c @@ -45,6 +45,14 @@ void serviceStatusLeds(void) { } } +void turnOffAllStatusLeds(void) { + funDigitalWrite(PIN_PWR, FUN_HIGH); + funDigitalWrite(PIN_INIT, FUN_HIGH); + funDigitalWrite(PIN_RDY, FUN_HIGH); + funDigitalWrite(PIN_RUN, FUN_HIGH); + funDigitalWrite(PIN_IDLE, FUN_HIGH); +} + void testCaseLEDStartupPattern(void) { static TestCaseState demoStates[5]; for (int k = 0; k < 4; k++) { @@ -64,3 +72,78 @@ void testCaseLEDStartupPattern(void) { for (int j = 0; j < 5; j++) demoStates[j] = TC_NO_RESULT; setTestCaseResult(demoStates); } + +// Demo Mode: This should show activity on the status LEDs +// and cycle through test case LEDs in a pattern designed +// to catch the eye. Doesn't reflect any real test results. +void demoMode(void) { + // Show sweeping pattern on status LEDs, repeat 5 times + for (int i = 0; i < 10; i++) { + for (int i = 0; i < 5; i++) { + funDigitalWrite(PIN_PWR, (i == 0) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_INIT, (i == 1) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_RDY, (i == 2) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_RUN, (i == 3) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_IDLE, (i == 4) ? FUN_LOW : FUN_HIGH); + waitTicks(30); + } + } + + // Demo mode on test case LEDs + static TestCaseState demoStates[5]; + for (int k = 0; k < 4; k++) { + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) demoStates[j] = TC_NO_RESULT; + demoStates[i] = TC_PASS; + setTestCaseResult(demoStates); + waitTicks(30); + } + for (int i = 4; i >= 0; i--) { + for (int j = 0; j < 5; j++) demoStates[j] = TC_NO_RESULT; + demoStates[i] = TC_FAIL; + setTestCaseResult(demoStates); + waitTicks(30); + } + } + for (int j = 0; j < 5; j++) demoStates[j] = TC_NO_RESULT; + setTestCaseResult(demoStates); + + // call testCaseLEDStartupPattern(); + testCaseLEDStartupPattern(); + + // Do 5 sweeps back and forth on status LEDs sweep up then down then repeat + for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { + funDigitalWrite(PIN_PWR, (i == 0) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_INIT, (i == 1) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_RDY, (i == 2) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_RUN, (i == 3) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_IDLE, (i == 4) ? FUN_LOW : FUN_HIGH); + waitTicks(30); + } + for (int i = 4; i >= 0; i--) { + funDigitalWrite(PIN_PWR, (i == 0) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_INIT, (i == 1) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_RDY, (i == 2) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_RUN, (i == 3) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_IDLE, (i == 4) ? FUN_LOW : FUN_HIGH); + waitTicks(30); + } + } + + testCaseLEDStartupPattern(); + + // Show backwards sweeping pattern on status LEDs, repeat 5 times + for (int i = 0; i < 10; i++) { + for (int i = 4; i >= 0; i--) { + funDigitalWrite(PIN_PWR, (i == 0) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_INIT, (i == 1) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_RDY, (i == 2) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_RUN, (i == 3) ? FUN_LOW : FUN_HIGH); + funDigitalWrite(PIN_IDLE, (i == 4) ? FUN_LOW : FUN_HIGH); + waitTicks(30); + } + } + + testCaseLEDStartupPattern(); +} diff --git a/software/firmware/tester_runtime/src/leds.h b/software/firmware/tester_runtime/src/leds.h index 42ad518..2710b53 100644 --- a/software/firmware/tester_runtime/src/leds.h +++ b/software/firmware/tester_runtime/src/leds.h @@ -12,8 +12,10 @@ extern volatile const TestCaseState* activeStates; #include "test_cases.h" void serviceStatusLeds(void); +void turnOffAllStatusLeds(void); void setTestCaseResult(const TestCaseState states[5]); void initTestCaseStates(void); void testCaseLEDStartupPattern(void); +void demoMode(void); #endif // LEDS_H diff --git a/software/firmware/tester_runtime/src/main.c b/software/firmware/tester_runtime/src/main.c index f6c511f..539adb9 100644 --- a/software/firmware/tester_runtime/src/main.c +++ b/software/firmware/tester_runtime/src/main.c @@ -1,3 +1,7 @@ +#include "utils.h" +// Prototypes for functions used but not declared +bool allTestsPassed(void); +void triggerPOVEasterEgg(void); #include "ch32fun.h" #include #include "globals.h" @@ -41,10 +45,17 @@ static void startupSequence(void) { startupSequence(); waitTicks(100); while (1) { - serviceStatusLeds(); + serviceStatusLeds(); ///Update status LEDs + // Check for button combos to start tests, demo mode, or PoV easter egg if (!testActive && buttons[3].pressed) { test_cases_start(currentTest); - } + } else if (!testActive && buttons[0].pressed && buttons[2].pressed) { + while(1) { + demoMode(); + } + } else if (allTestsPassed() && buttons[1].pressed && buttons[2].pressed) { + triggerPOVEasterEgg(); + } test_cases_monitor_inputs(); __WFI(); } diff --git a/software/firmware/tester_runtime/src/test_cases.c b/software/firmware/tester_runtime/src/test_cases.c index af90ac9..78990fc 100644 --- a/software/firmware/tester_runtime/src/test_cases.c +++ b/software/firmware/tester_runtime/src/test_cases.c @@ -300,7 +300,7 @@ static const TestCaseDef testCases[] = { { 5000, tc4_init, tc4_update, tc4_eval }, { 30000, tc5_init, tc5_update, tc5_eval } }; -static const size_t NUM_TEST_CASES = sizeof(testCases) / sizeof(testCases[0]); +const size_t NUM_TEST_CASES = sizeof(testCases) / sizeof(testCases[0]); // ===== Test Case Runner ===== diff --git a/software/firmware/tester_runtime/src/test_cases.h b/software/firmware/tester_runtime/src/test_cases.h index cc2cace..301a032 100644 --- a/software/firmware/tester_runtime/src/test_cases.h +++ b/software/firmware/tester_runtime/src/test_cases.h @@ -1,6 +1,7 @@ #ifndef TEST_CASES_H #define TEST_CASES_H +#include #include #include @@ -25,4 +26,6 @@ extern TestCaseState tcResults[5]; extern uint8_t currentTest; extern bool testActive; +extern const size_t NUM_TEST_CASES; + #endif // TEST_CASES_H diff --git a/software/firmware/tester_runtime/src/utils.c b/software/firmware/tester_runtime/src/utils.c new file mode 100644 index 0000000..c971b93 --- /dev/null +++ b/software/firmware/tester_runtime/src/utils.c @@ -0,0 +1,183 @@ +#include // For strlen +#include "ch32fun.h" +#include "pins.h" +#include "utils.h" +#include "test_cases.h" +#include "leds.h" + +bool allTestsPassed(void) { + for (size_t i = 0; i < NUM_TEST_CASES; i++) { + if (tcResults[i] != TC_PASS) return false; + } + return true; +} + +// PoV Easter Egg +// Simple 5x4 font for PoV +// Message shows BUG in simple font using the following 4x5 font data +// B = 11111, 10101, 10101, 01010, +// U = 01111, 10000, 10000, 01111, +// G = 01110, 10001, 11001, 01010, +// Gap = 00000 + +static const uint8_t pov_font_B[5] = { + 0b11111, // Column 0 + 0b10101, // Column 1 + 0b10101, // Column 2 + 0b01010, // Column 3 +}; + +static const uint8_t pov_font_U[5] = { + 0b01111, // Column 0 + 0b10000, // Column 1 + 0b10000, // Column 2 + 0b01111, // Column 0 +}; + +static const uint8_t pov_font_G[5] = { + 0b01110, // Column 0 + 0b10001, // Column 1 + 0b11001, // Column 2 + 0b01010, // Column 3 +}; + +// M +static const uint8_t pov_font_M[5] = { + 0b11111, // Column 0 + 0b00010, // Column 1 + 0b00100, // Column 2 + 0b00010, // Column 1 + 0b11111, // Column 0 +}; + +// O is also used for 0 +static const uint8_t pov_font_O[5] = { + 0b01110, // Column 0 + 0b10001, // Column 1 + 0b10001, // Column 2 + 0b01110, // Column 3 +}; + +// T +static const uint8_t pov_font_T[5] = { + + 0b00001, // Column 1 + 0b00001, // Column 2 + 0b11111, // Column 3 + 0b00001, // Column 4 + 0b00001, // Column 5 +}; + +// 2 +static const uint8_t pov_font_2[5] = { + 0b11010, // Column 0 + 0b11001, // Column 1 + 0b11101, // Column 2 + 0b10110, // Column 3 +}; + +// 5 +static const uint8_t pov_font_5[5] = { + 0b10111, // Column 0 + 0b10101, // Column 1 + 0b10101, // Column 2 + 0b01001, // Column 3 +}; + +// Space is a special case +static const uint8_t pov_font_space[1] = { + 0b00000, // Column 0 +}; + +static const char* povMessage = "MOT 2025 "; + +void displayPOVChar(char c) { + const uint8_t* pattern = NULL; + int numCols = 0; + // Select pattern and number of columns for each character + switch (c) { + case 'B': + pattern = pov_font_B; + numCols = 4; + break; + case 'U': + pattern = pov_font_U; + numCols = 4; + break; + case 'G': + pattern = pov_font_G; + numCols = 4; + break; + case 'M': + pattern = pov_font_M; + numCols = 5; + break; + case 'O': + pattern = pov_font_O; + numCols = 4; + break; + case 'T': + pattern = pov_font_T; + numCols = 5; + break; + case '0': + pattern = pov_font_O; + numCols = 4; + break; + case '2': + pattern = pov_font_2; + numCols = 4; + break; + case '5': + pattern = pov_font_5; + numCols = 4; + break; + case ' ': // gap/space + pattern = pov_font_space; + numCols = 1; + break; + default: + pattern = pov_font_space; + numCols = 1; + break; + } + // Display each column of the character using status LEDs (active low) + for (int col = 0; col < numCols; col++) { + // Set all status LEDs OFF (inactive/high) + funDigitalWrite(PIN_PWR, 1); + funDigitalWrite(PIN_INIT, 1); + funDigitalWrite(PIN_RDY, 1); + funDigitalWrite(PIN_RUN, 1); + funDigitalWrite(PIN_IDLE, 1); + + if (pattern && (pattern[col] & 0x1F)) { + if (pattern[col] & (1 << 0)) funDigitalWrite(PIN_PWR, 0); // ON (active low) + if (pattern[col] & (1 << 1)) funDigitalWrite(PIN_INIT, 0); + if (pattern[col] & (1 << 2)) funDigitalWrite(PIN_RDY, 0); + if (pattern[col] & (1 << 3)) funDigitalWrite(PIN_RUN, 0); + if (pattern[col] & (1 << 4)) funDigitalWrite(PIN_IDLE, 0); + } + Delay_Ms(2); // Adjust timing for PoV effect + } + // Always add a 1-column gap after each character + funDigitalWrite(PIN_PWR, 1); + funDigitalWrite(PIN_INIT, 1); + funDigitalWrite(PIN_RDY, 1); + funDigitalWrite(PIN_RUN, 1); + funDigitalWrite(PIN_IDLE, 1); + Delay_Ms(2); +} + +void triggerPOVEasterEgg(void) { + // turn off all other LEDs including PWR, INIT, RDY + turnOffAllStatusLeds(); + // turn off test case LEDs + setTestCaseResult((TestCaseState[5]){ TC_NO_RESULT, TC_NO_RESULT, TC_NO_RESULT, TC_NO_RESULT, TC_NO_RESULT }); + + while (1) { + for (size_t i = 0; i < strlen(povMessage); i++) { + displayPOVChar(povMessage[i]); + } + } + +} diff --git a/software/firmware/tester_runtime/src/utils.h b/software/firmware/tester_runtime/src/utils.h new file mode 100644 index 0000000..03dd676 --- /dev/null +++ b/software/firmware/tester_runtime/src/utils.h @@ -0,0 +1,19 @@ +// utils.h – Easter egg and POV rendering utilities +// Exposes functions for test result checking and LED-based message display + +#ifndef UTILS_H +#define UTILS_H + +#include // For bool +#include // For size_t +#include "test_cases.h" // For TestCaseState + +extern const size_t NUM_TEST_CASES; +extern TestCaseState tcResults[]; + +// Public function declarations +bool allTestsPassed(void); +void displayPOVChar(char c); +void triggerPOVEasterEgg(void); + +#endif // UTILS_H