From 78af43ada6b4cb00e4059b66309c0de2dff3c8cc Mon Sep 17 00:00:00 2001 From: Mark Benson Date: Tue, 16 Sep 2025 16:21:01 +0100 Subject: [PATCH 1/5] Basic PoV PoC --- software/firmware/tester_runtime/src/leds.c | 8 ++ software/firmware/tester_runtime/src/leds.h | 1 + software/firmware/tester_runtime/src/main.c | 8 +- .../firmware/tester_runtime/src/test_cases.c | 2 +- .../firmware/tester_runtime/src/test_cases.h | 3 + software/firmware/tester_runtime/src/utils.c | 119 ++++++++++++++++++ software/firmware/tester_runtime/src/utils.h | 19 +++ 7 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 software/firmware/tester_runtime/src/utils.c create mode 100644 software/firmware/tester_runtime/src/utils.h diff --git a/software/firmware/tester_runtime/src/leds.c b/software/firmware/tester_runtime/src/leds.c index 06c5ab1..6c194e0 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++) { diff --git a/software/firmware/tester_runtime/src/leds.h b/software/firmware/tester_runtime/src/leds.h index 42ad518..f347337 100644 --- a/software/firmware/tester_runtime/src/leds.h +++ b/software/firmware/tester_runtime/src/leds.h @@ -12,6 +12,7 @@ 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); diff --git a/software/firmware/tester_runtime/src/main.c b/software/firmware/tester_runtime/src/main.c index f6c511f..550198a 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" @@ -44,7 +48,9 @@ static void startupSequence(void) { serviceStatusLeds(); if (!testActive && buttons[3].pressed) { test_cases_start(currentTest); - } + } 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..2b268ab --- /dev/null +++ b/software/firmware/tester_runtime/src/utils.c @@ -0,0 +1,119 @@ +#include // For strlen +#include "ch32fun.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 +}; + +// Space is a special case +static const uint8_t pov_font_space[1] = { + 0b00000, // Column 0 +}; + +static const char* povMessage = "BUG"; + +void displayPOVChar(char c) { + TestCaseState povStates[5]; + 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 ' ': // gap/space + pattern = pov_font_space; + numCols = 1; + break; + default: + pattern = pov_font_space; + numCols = 1; + break; + } + // Display each column of the character + for (int col = 0; col < numCols; col++) { + // Each bit in pattern[col] corresponds to a status LED (active low) + // Bit 0: PIN_PWR, Bit 1: PIN_INIT, Bit 2: PIN_RDY, Bit 3: PIN_RUN, Bit 4: PIN_IDLE + // Set all status LEDs OFF (inactive/high) + GPIO_SetBits(PIN_PWR); + GPIO_SetBits(PIN_INIT); + GPIO_SetBits(PIN_RDY); + GPIO_SetBits(PIN_RUN); + GPIO_SetBits(PIN_IDLE); + + if (pattern && (pattern[col] & 0x1F)) { + if (pattern[col] & (1 << 0)) GPIO_ResetBits(PIN_PWR); // ON (active low) + if (pattern[col] & (1 << 1)) GPIO_ResetBits(PIN_INIT); + if (pattern[col] & (1 << 2)) GPIO_ResetBits(PIN_RDY); + if (pattern[col] & (1 << 3)) GPIO_ResetBits(PIN_RUN); + if (pattern[col] & (1 << 4)) GPIO_ResetBits(PIN_IDLE); + } + Delay_Ms(10); // Adjust timing for PoV effect + } + // Always add a 1-column gap after each character (except if already a gap) + if (numCols == 4) { + GPIO_SetBits(PIN_PWR); + GPIO_SetBits(PIN_INIT); + GPIO_SetBits(PIN_RDY); + GPIO_SetBits(PIN_RUN); + GPIO_SetBits(PIN_IDLE); + Delay_Ms(5); + } +} + +void triggerPOVEasterEgg(void) { + // turn off all other LEDs including PWR, INIT, RDY + turnOffAllStatusLeds(); + + while (1) { + for (size_t i = 0; i < strlen(povMessage); i++) { + displayPOVChar(povMessage[i]); + // Delay_Ms(20); // Adjust timing for motion blur + } + } + +} 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 From 61bc24adefccc84ef73f8e7d2c5b29e9565db8c8 Mon Sep 17 00:00:00 2001 From: Mark Benson Date: Tue, 16 Sep 2025 16:21:45 +0100 Subject: [PATCH 2/5] Update utils.c --- software/firmware/tester_runtime/src/utils.c | 30 +++++++------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/software/firmware/tester_runtime/src/utils.c b/software/firmware/tester_runtime/src/utils.c index 2b268ab..fe85fb4 100644 --- a/software/firmware/tester_runtime/src/utils.c +++ b/software/firmware/tester_runtime/src/utils.c @@ -76,31 +76,21 @@ void displayPOVChar(char c) { } // Display each column of the character for (int col = 0; col < numCols; col++) { - // Each bit in pattern[col] corresponds to a status LED (active low) - // Bit 0: PIN_PWR, Bit 1: PIN_INIT, Bit 2: PIN_RDY, Bit 3: PIN_RUN, Bit 4: PIN_IDLE - // Set all status LEDs OFF (inactive/high) - GPIO_SetBits(PIN_PWR); - GPIO_SetBits(PIN_INIT); - GPIO_SetBits(PIN_RDY); - GPIO_SetBits(PIN_RUN); - GPIO_SetBits(PIN_IDLE); - - if (pattern && (pattern[col] & 0x1F)) { - if (pattern[col] & (1 << 0)) GPIO_ResetBits(PIN_PWR); // ON (active low) - if (pattern[col] & (1 << 1)) GPIO_ResetBits(PIN_INIT); - if (pattern[col] & (1 << 2)) GPIO_ResetBits(PIN_RDY); - if (pattern[col] & (1 << 3)) GPIO_ResetBits(PIN_RUN); - if (pattern[col] & (1 << 4)) GPIO_ResetBits(PIN_IDLE); + for (int i = 0; i < 5; i++) povStates[i] = TC_NO_RESULT; + if (pattern && (pattern[col] & 0x1F)) { // 5 bits for 5 LEDs + for (int row = 0; row < 5; row++) { + if (pattern[col] & (1 << row)) { + povStates[row] = TC_PASS; + } + } } + setTestCaseResult(povStates); Delay_Ms(10); // Adjust timing for PoV effect } // Always add a 1-column gap after each character (except if already a gap) if (numCols == 4) { - GPIO_SetBits(PIN_PWR); - GPIO_SetBits(PIN_INIT); - GPIO_SetBits(PIN_RDY); - GPIO_SetBits(PIN_RUN); - GPIO_SetBits(PIN_IDLE); + for (int i = 0; i < 5; i++) povStates[i] = TC_NO_RESULT; + setTestCaseResult(povStates); Delay_Ms(5); } } From a0ab723fd91fa8b0211d25b9df7217b94466238a Mon Sep 17 00:00:00 2001 From: Mark Benson Date: Tue, 16 Sep 2025 16:41:59 +0100 Subject: [PATCH 3/5] Use Status LEDs to avoid test case LED matrix timing issues --- software/firmware/tester_runtime/src/utils.c | 38 ++++++++++++-------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/software/firmware/tester_runtime/src/utils.c b/software/firmware/tester_runtime/src/utils.c index fe85fb4..ac22a03 100644 --- a/software/firmware/tester_runtime/src/utils.c +++ b/software/firmware/tester_runtime/src/utils.c @@ -1,5 +1,6 @@ #include // For strlen #include "ch32fun.h" +#include "pins.h" #include "utils.h" #include "test_cases.h" #include "leds.h" @@ -48,7 +49,6 @@ static const uint8_t pov_font_space[1] = { static const char* povMessage = "BUG"; void displayPOVChar(char c) { - TestCaseState povStates[5]; const uint8_t* pattern = NULL; int numCols = 0; // Select pattern and number of columns for each character @@ -74,30 +74,40 @@ void displayPOVChar(char c) { numCols = 1; break; } - // Display each column of the character + // Display each column of the character using status LEDs (active low) for (int col = 0; col < numCols; col++) { - for (int i = 0; i < 5; i++) povStates[i] = TC_NO_RESULT; - if (pattern && (pattern[col] & 0x1F)) { // 5 bits for 5 LEDs - for (int row = 0; row < 5; row++) { - if (pattern[col] & (1 << row)) { - povStates[row] = TC_PASS; - } - } + // 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); } - setTestCaseResult(povStates); - Delay_Ms(10); // Adjust timing for PoV effect + Delay_Ms(2); // Adjust timing for PoV effect } // Always add a 1-column gap after each character (except if already a gap) if (numCols == 4) { - for (int i = 0; i < 5; i++) povStates[i] = TC_NO_RESULT; - setTestCaseResult(povStates); - Delay_Ms(5); + 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++) { From e6a51ec62e8040a0f3afddaabf40320573a69b5d Mon Sep 17 00:00:00 2001 From: Mark Benson Date: Fri, 19 Sep 2025 10:26:49 +0100 Subject: [PATCH 4/5] Add 'MoT 2025' PoV message Eye catching led flashing mode --- software/firmware/tester_runtime/src/utils.c | 86 +++++++++++++++++--- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/software/firmware/tester_runtime/src/utils.c b/software/firmware/tester_runtime/src/utils.c index ac22a03..c971b93 100644 --- a/software/firmware/tester_runtime/src/utils.c +++ b/software/firmware/tester_runtime/src/utils.c @@ -41,12 +41,55 @@ static const uint8_t pov_font_G[5] = { 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 = "BUG"; +static const char* povMessage = "MOT 2025 "; void displayPOVChar(char c) { const uint8_t* pattern = NULL; @@ -65,6 +108,30 @@ void displayPOVChar(char c) { 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; @@ -92,15 +159,13 @@ void displayPOVChar(char c) { } Delay_Ms(2); // Adjust timing for PoV effect } - // Always add a 1-column gap after each character (except if already a gap) - if (numCols == 4) { - funDigitalWrite(PIN_PWR, 1); - funDigitalWrite(PIN_INIT, 1); - funDigitalWrite(PIN_RDY, 1); - funDigitalWrite(PIN_RUN, 1); - funDigitalWrite(PIN_IDLE, 1); - Delay_Ms(2); - } + // 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) { @@ -112,7 +177,6 @@ void triggerPOVEasterEgg(void) { while (1) { for (size_t i = 0; i < strlen(povMessage); i++) { displayPOVChar(povMessage[i]); - // Delay_Ms(20); // Adjust timing for motion blur } } From 4b57445bf5847cf5fdf1e95d8ebe4bb904d08211 Mon Sep 17 00:00:00 2001 From: Mark Benson Date: Fri, 19 Sep 2025 10:27:20 +0100 Subject: [PATCH 5/5] Add a rough and ready demo mode --- software/firmware/tester_runtime/src/leds.c | 75 +++++++++++++++++++++ software/firmware/tester_runtime/src/leds.h | 1 + software/firmware/tester_runtime/src/main.c | 9 ++- 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/software/firmware/tester_runtime/src/leds.c b/software/firmware/tester_runtime/src/leds.c index 6c194e0..2b525b1 100644 --- a/software/firmware/tester_runtime/src/leds.c +++ b/software/firmware/tester_runtime/src/leds.c @@ -72,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 f347337..2710b53 100644 --- a/software/firmware/tester_runtime/src/leds.h +++ b/software/firmware/tester_runtime/src/leds.h @@ -16,5 +16,6 @@ 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 550198a..539adb9 100644 --- a/software/firmware/tester_runtime/src/main.c +++ b/software/firmware/tester_runtime/src/main.c @@ -45,10 +45,15 @@ 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 (allTestsPassed() && buttons[1].pressed && buttons[2].pressed) { + } 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();