diff --git a/assets/kid0.png b/assets/test/kid0.png similarity index 100% rename from assets/kid0.png rename to assets/test/kid0.png diff --git a/assets/test/kid1.png b/assets/test/kid1.png new file mode 100644 index 000000000..992a1350e Binary files /dev/null and b/assets/test/kid1.png differ diff --git a/assets/test/stereo_test.mid b/assets/test/stereo_test.mid new file mode 100644 index 000000000..6f4bc9c44 Binary files /dev/null and b/assets/test/stereo_test.mid differ diff --git a/components/hdw-bzr/hdw-bzr.c b/components/hdw-bzr/hdw-bzr.c index 4603db60a..b58465729 100644 --- a/components/hdw-bzr/hdw-bzr.c +++ b/components/hdw-bzr/hdw-bzr.c @@ -197,7 +197,7 @@ void deinitBuzzer(void) * @brief Set the buzzer's bgm volume. setBgmVolumeSetting() should be called instead if the new volume should be * persistent through a reboot. * - * @param vol The background volume, 0 to 13 + * @param vol The background volume, 0 to MAX_VOLUME */ void bzrSetBgmVolume(uint16_t vol) { @@ -208,7 +208,7 @@ void bzrSetBgmVolume(uint16_t vol) * @brief Set the buzzer's sfx volume. setSfxVolumeSetting() should be called instead if the new volume should be * persistent through a reboot. * - * @param vol The background volume, 0 to 13 + * @param vol The background volume, 0 to MAX_VOLUME */ void bzrSetSfxVolume(uint16_t vol) { diff --git a/components/hdw-bzr/include/hdw-bzr.h b/components/hdw-bzr/include/hdw-bzr.h index 71ea7e6e9..e5bb95f11 100644 --- a/components/hdw-bzr/include/hdw-bzr.h +++ b/components/hdw-bzr/include/hdw-bzr.h @@ -75,6 +75,8 @@ /** @brief The number of physical buzzers */ #define NUM_BUZZERS 2 +#define MAX_VOLUME 13 + /** * @brief Frequencies for all the notes, in hertz. * diff --git a/components/hdw-mic/include/hdw-mic.h b/components/hdw-mic/include/hdw-mic.h index 029e06583..49c35ae5f 100644 --- a/components/hdw-mic/include/hdw-mic.h +++ b/components/hdw-mic/include/hdw-mic.h @@ -74,6 +74,8 @@ /// The maximum number of bytes read by the ADC in one go #define ADC_READ_LEN 512 +#define MAX_MIC_GAIN 7 + //============================================================================== // Function Prototypes //============================================================================== diff --git a/components/hdw-tft/hdw-tft.c b/components/hdw-tft/hdw-tft.c index 8df456766..8888ab3b8 100644 --- a/components/hdw-tft/hdw-tft.c +++ b/components/hdw-tft/hdw-tft.c @@ -126,7 +126,7 @@ static esp_lcd_panel_io_handle_t tft_io_handle = NULL; * @brief Set TFT Backlight brightness. setTftBrightnessSetting() should be called instead if the new volume should be * persistent through a reboot. * - * @param intensity Sets the brightness 0-7 + * @param intensity The brightness, 0 to MAX_TFT_BRIGHTNESS * * @return value is 0 if OK nonzero if error. */ diff --git a/components/hdw-tft/include/hdw-tft.h b/components/hdw-tft/include/hdw-tft.h index 9d09d7fff..670b96ad2 100644 --- a/components/hdw-tft/include/hdw-tft.h +++ b/components/hdw-tft/include/hdw-tft.h @@ -85,6 +85,9 @@ #include "palette.h" +/// The maximum brightness setting of the TFT +#define MAX_TFT_BRIGHTNESS 7 + #if defined(CONFIG_ST7735_160x80) #define TFT_WIDTH 160 #define TFT_HEIGHT 80 diff --git a/emulator/src/components/hdw-bzr/hdw-bzr.c b/emulator/src/components/hdw-bzr/hdw-bzr.c index a62be5ba4..555a28c34 100644 --- a/emulator/src/components/hdw-bzr/hdw-bzr.c +++ b/emulator/src/components/hdw-bzr/hdw-bzr.c @@ -136,7 +136,7 @@ void deinitBuzzer(void) /** * @brief Set the buzzer's bgm volume * - * @param vol The background volume, 0 to 13 + * @param vol The background volume, 0 to MAX_VOLUME */ void bzrSetBgmVolume(uint16_t vol) { @@ -146,7 +146,7 @@ void bzrSetBgmVolume(uint16_t vol) /** * @brief Set the buzzer's sfx volume * - * @param vol The background volume, 0 to 13 + * @param vol The background volume, 0 to MAX_VOLUME */ void bzrSetSfxVolume(uint16_t vol) { diff --git a/emulator/src/components/hdw-tft/hdw-tft.c b/emulator/src/components/hdw-tft/hdw-tft.c index 3fd0bae79..9f58c53f1 100644 --- a/emulator/src/components/hdw-tft/hdw-tft.c +++ b/emulator/src/components/hdw-tft/hdw-tft.c @@ -257,7 +257,7 @@ void drawDisplayTft(fnBackgroundDrawCallback_t fnBackgroundDrawCallback) /** * @brief Set TFT Backlight brightness. * - * @param intensity Sets the brightness 0-7 + * @param intensity The brightness, 0 to MAX_TFT_BRIGHTNESS * * @return value is 0 if OK nonzero if error. */ diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index ff7606806..0b7a0e96b 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -17,16 +17,25 @@ idf_component_register(SRCS "swadge2024.c" "menu/menu_utils.c" "menu/menuLogbookRenderer.c" "modes/accelTest/accelTest.c" + "modes/breakout/aabb_utils.c" + "modes/breakout/breakout.c" + "modes/breakout/entity.c" + "modes/breakout/entityManager.c" + "modes/breakout/gameData.c" + "modes/breakout/soundManager.c" + "modes/breakout/starfield.c" + "modes/breakout/tilemap.c" "modes/colorchord/colorchord.c" "modes/dance/dance.c" "modes/dance/portableDance.c" "modes/demo/demoMode.c" + "modes/factoryTest/factoryTest.c" "modes/gamepad/gamepad.c" + "modes/jukebox/jukebox.c" "modes/lumberjack/lumberjack.c" "modes/lumberjack/lumberjackGame.c" "modes/lumberjack/lumberjackEntity.c" "modes/lumberjack/lumberjackPlayer.c" - "modes/jukebox/jukebox.c" "modes/mainMenu/mainMenu.c" "modes/marbles/marbles.c" "modes/mfpaint/mode_paint.c" @@ -41,35 +50,26 @@ idf_component_register(SRCS "swadge2024.c" "modes/mfpaint/paint_ui.c" "modes/mfpaint/paint_util.c" "modes/mfpaint/px_stack.c" - "modes/pushy/pushy.c" - "modes/pong/pong.c" - "modes/breakout/breakout.c" - "modes/breakout/tilemap.c" - "modes/breakout/entityManager.c" - "modes/breakout/entity.c" - "modes/breakout/gameData.c" - "modes/breakout/soundManager.c" - "modes/breakout/aabb_utils.c" - "modes/breakout/starfield.c" "modes/platformer/plEntity.c" "modes/platformer/plEntityManager.c" "modes/platformer/plGameData.c" "modes/platformer/mode_platformer.c" "modes/platformer/plTilemap.c" "modes/platformer/plSoundManager.c" - "modes/touchTest/touchTest.c" + "modes/pong/pong.c" + "modes/pushy/pushy.c" "modes/quickSettings/menuQuickSettingsRenderer.c" "modes/quickSettings/quickSettings.c" "modes/ray/fp_math.c" "modes/ray/mode_ray.c" - "modes/ray/ray_renderer.c" + "modes/ray/ray_dialog.c" "modes/ray/ray_map.c" "modes/ray/ray_object.c" - "modes/ray/ray_tex_manager.c" - "modes/ray/ray_player.c" - "modes/ray/ray_dialog.c" "modes/ray/ray_pause.c" + "modes/ray/ray_renderer.c" "modes/ray/ray_script.c" + "modes/ray/ray_tex_manager.c" + "modes/ray/ray_player.c" "modes/ray/ray_warp_screen.c" "modes/ray/enemies/ray_enemy.c" "modes/ray/enemies/ray_enemy.h" @@ -85,11 +85,12 @@ idf_component_register(SRCS "swadge2024.c" "modes/ray/enemies/ray_enemy_normal.h" "modes/ray/enemies/ray_enemy_strong.c" "modes/ray/enemies/ray_enemy_strong.h" - "modes/tunernome/tunernome.c" "modes/soko/soko_game.c" "modes/soko/soko_gamerules.c" "modes/soko/soko_input.c" "modes/soko/soko.c" + "modes/touchTest/touchTest.c" + "modes/tunernome/tunernome.c" "utils/color_utils.c" "utils/geometry.c" "utils/linked_list.c" @@ -121,26 +122,26 @@ idf_component_register(SRCS "swadge2024.c" "./display" "./menu" "./modes/accelTest" - "./modes/touchTest" + "./modes/breakout" "./modes/colorchord" "./modes/dance" + "./modes/demo" + "./modes/factoryTest" "./modes/gamepad" "./modes/jukebox" - "./modes/pushy" - "./modes/pong" - "./modes/breakout" - "./modes/platformer" - "./modes/tunernome" - "./modes/mainMenu" - "./modes/demo" - "./modes/soko" "./modes/lumberjack" + "./modes/mainMenu" "./modes/marbles" "./modes/mfpaint" + "./modes/platformer" + "./modes/pong" + "./modes/pushy" + "./modes/quickSettings" "./modes/ray" "./modes/ray/enemies" - "./modes/demo" - "./modes/quickSettings") + "./modes/soko" + "./modes/touchTest" + "./modes/tunernome") function(spiffs_file_preprocessor) add_custom_target(spiffs_preprocessor ALL diff --git a/main/modes/factoryTest/factoryTest.c b/main/modes/factoryTest/factoryTest.c new file mode 100644 index 000000000..4ebf3de06 --- /dev/null +++ b/main/modes/factoryTest/factoryTest.c @@ -0,0 +1,812 @@ +//============================================================================== +// Includes +//============================================================================== + +#include + +#include +#include + +#include "embeddedOut.h" +#include "hdw-bzr.h" +#include "hdw-led.h" +#include "settingsManager.h" +#include "touchTest.h" + +#include "factoryTest.h" +#include "mainMenu.h" + +//============================================================================== +// Defines +//============================================================================== + +#define BTN_RADIUS 10 +#define BTN_DIA (BTN_RADIUS * 2) +#define AB_SEP 6 +#define DPAD_SEP 15 + +#define ACCEL_BAR_HEIGHT 8 +#define ACCEL_BAR_SEP 1 +#define MAX_ACCEL_BAR_W 60 + +#define TOUCHBAR_WIDTH 60 +#define TOUCHBAR_HEIGHT 60 +#define TOUCHBAR_Y_OFF 32 + +//============================================================================== +// Enums +//============================================================================== + +typedef enum +{ + BTN_NOT_PRESSED, + BTN_PRESSED, + BTN_RELEASED +} testButtonState_t; + +typedef enum +{ + R_RISING, + R_FALLING, + G_RISING, + G_FALLING, + B_RISING, + B_FALLING +} testLedState_t; + +//============================================================================== +// Functions Prototypes +//============================================================================== + +void testEnterMode(void); +void testExitMode(void); +void testMainLoop(int64_t elapsedUs); +void testAudioCb(uint16_t* samples, uint32_t sampleCnt); +void testProcessButtons(buttonEvt_t* evt); +uint8_t touchJoystickToTouchIdx(touchJoystick_t touchJoystick); +touchJoystick_t touchIdxToTouchJoystick(uint8_t touchIdx); +void testReadAndValidateTouch(void); +void testReadAndValidateAccelerometer(void); + +void plotButtonState(int16_t x, int16_t y, testButtonState_t state); +static void touchFillCircleSegments(int16_t x, int16_t y, int16_t r, int16_t segs, bool center); + +//============================================================================== +// Variables +//============================================================================== + +typedef struct +{ + font_t ibm_vga8; + wsg_t kd_idle0; + wsg_t kd_idle1; + uint64_t tSpriteElapsedUs; + uint8_t spriteFrame; + // Microphone test + dft32_data dd; + embeddedNf_data end; + embeddedOut_data eod; + uint8_t samplesProcessed; + uint16_t maxValue; + // Buzzers + song_t song; + // Button + testButtonState_t buttonStates[8]; + // Touch, as an 8-way joystick with center deadzone + testButtonState_t touchStates[9]; + uint8_t lastTouchStateIdx; + touchJoystick_t touchJoystick; + bool touched; + int32_t touchAngle; + int32_t touchRadius; + // Accelerometer + int16_t x; + int16_t y; + int16_t z; + // LED + led_t cLed; + testLedState_t ledState; + uint64_t tLedElapsedUs; + // Test results + bool buttonsPassed; + bool touchPassed; + bool accelPassed; + bool bzrMicPassed; +} factoryTest_t; + +factoryTest_t* test; + +swadgeMode_t factoryTestMode = { + .modeName = "Factory Test", + .wifiMode = NO_WIFI, + .overrideUsb = false, + .usesAccelerometer = true, + .usesThermometer = true, + .overrideSelectBtn = true, + .fnEnterMode = testEnterMode, + .fnExitMode = testExitMode, + .fnMainLoop = testMainLoop, + .fnAudioCallback = testAudioCb, + .fnBackgroundDrawCallback = NULL, + .fnEspNowRecvCb = NULL, + .fnEspNowSendCb = NULL, + .fnAdvancedUSB = NULL, +}; + +//============================================================================== +// Functions +//============================================================================== + +/** + * @brief Enter test mode, allocate memory, initialize code + */ +void testEnterMode(void) +{ + // Allocate memory for this mode + test = (factoryTest_t*)calloc(1, sizeof(factoryTest_t)); + + // Load a font + loadFont("ibm_vga8.font", &test->ibm_vga8, false); + + // Load a sprite + loadWsg("kid0.wsg", &test->kd_idle0, false); + loadWsg("kid1.wsg", &test->kd_idle1, false); + + // Init last touchState index to indicate no previous state + test->lastTouchStateIdx = UINT8_MAX; + + // Init CC + InitColorChord(&test->end, &test->dd); + test->maxValue = 1; + + // Temporarily set the buzzer to full volume + bzrSetBgmVolume(MAX_VOLUME); + bzrSetSfxVolume(MAX_VOLUME); + + // Set the mic to listen + setMicGainSetting(MAX_MIC_GAIN); + + // Play a song + loadSong("stereo_test.sng", &test->song, false); + bzrPlayBgm(&test->song, BZR_STEREO); + + // Set NVM to indicate test not passed yet + // setTestModePassedSetting(false); +} + +/** + * @brief Exit test mode, free memory + */ +void testExitMode(void) +{ + freeFont(&test->ibm_vga8); + freeWsg(&test->kd_idle0); + freeWsg(&test->kd_idle1); + freeSong(&test->song); + free(test); +} + +/** + * @brief This is the main loop, and it draws to the TFT + * + * @param elapsedUs unused + */ +void testMainLoop(int64_t elapsedUs __attribute__((unused))) +{ + // Process button events + buttonEvt_t evt = {0}; + while (checkButtonQueueWrapper(&evt)) + { + testProcessButtons(&evt); + } + + testReadAndValidateTouch(); + testReadAndValidateAccelerometer(); + + // Check for a test pass + if (test->buttonsPassed && test->touchPassed && test->accelPassed && test->bzrMicPassed) + { + // Set NVM to indicate the test passed + setTestModePassedSetting(true); + + if (getTestModePassedSetting()) + { + switchToSwadgeMode(&mainMenuMode); + } + } + + // Clear everything + clearPxTft(); + + // Draw the spectrum as a bar graph. Figure out bar and margin size + int16_t binWidth = (TFT_WIDTH / FIX_BINS); + int16_t binMargin = (TFT_WIDTH - (binWidth * FIX_BINS)) / 2; + + // Find the max value + for (uint16_t i = 0; i < FIX_BINS; i++) + { + if (test->end.fuzzed_bins[i] > test->maxValue) + { + test->maxValue = test->end.fuzzed_bins[i]; + } + } + + // Plot the bars + int32_t energy = 0; + for (uint16_t i = 0; i < FIX_BINS; i++) + { + energy += test->end.fuzzed_bins[i]; + uint8_t height = ((TFT_HEIGHT / 2) * test->end.fuzzed_bins[i]) / test->maxValue; + paletteColor_t color = test->bzrMicPassed ? c050 : c500; // paletteHsvToHex((i * 256) / FIX_BINS, 255, 255); + int16_t x0 = binMargin + (i * binWidth); + int16_t x1 = binMargin + ((i + 1) * binWidth); + // Big enough, fill an area + fillDisplayArea(x0, TFT_HEIGHT - height, x1, TFT_HEIGHT, color); + } + + // Check for a pass + if (energy > 100000) + { + test->bzrMicPassed = true; + } + + // Draw button states + int16_t centerLine = TFT_HEIGHT / 4; + int16_t btnX = BTN_DIA; + + // PB_LEFT + plotButtonState(btnX, centerLine, test->buttonStates[2]); + btnX += DPAD_SEP; + // PB_UP + plotButtonState(btnX, centerLine - DPAD_SEP, test->buttonStates[0]); + // PB_DOWN + plotButtonState(btnX, centerLine + DPAD_SEP, test->buttonStates[1]); + btnX += DPAD_SEP; + // PB_RIGHT + plotButtonState(btnX, centerLine, test->buttonStates[3]); + btnX += BTN_DIA + 4; + + // PB_SELECT + plotButtonState(btnX, centerLine, test->buttonStates[7]); + btnX += BTN_DIA + 2; + // PB_START + plotButtonState(btnX, centerLine, test->buttonStates[6]); + btnX += BTN_DIA + 4; + + // PB_B + plotButtonState(btnX, centerLine + AB_SEP, test->buttonStates[5]); + btnX += BTN_DIA + 2; + // PB_A + plotButtonState(btnX, centerLine - AB_SEP, test->buttonStates[4]); + + // Set up drawing accel bars + int16_t barY = TOUCHBAR_Y_OFF; + + paletteColor_t accelColor = c500; + if (test->accelPassed) + { + accelColor = c050; + } + + // Plot X accel + int16_t barWidth = ((test->x + 256) * MAX_ACCEL_BAR_W) / 512; + if (barWidth >= 0) + { + fillDisplayArea(TFT_WIDTH - barWidth, barY, TFT_WIDTH, barY + ACCEL_BAR_HEIGHT, accelColor); + } + barY += (ACCEL_BAR_HEIGHT + ACCEL_BAR_SEP); + + // Plot Y accel + barWidth = ((test->y + 256) * MAX_ACCEL_BAR_W) / 512; + if (barWidth >= 0) + { + fillDisplayArea(TFT_WIDTH - barWidth, barY, TFT_WIDTH, barY + ACCEL_BAR_HEIGHT, accelColor); + } + barY += (ACCEL_BAR_HEIGHT + ACCEL_BAR_SEP); + + // Plot Z accel + barWidth = ((test->z + 256) * MAX_ACCEL_BAR_W) / 512; + if (barWidth >= 0) + { + fillDisplayArea(TFT_WIDTH - barWidth, barY, TFT_WIDTH, barY + ACCEL_BAR_HEIGHT, accelColor); + } + barY += (ACCEL_BAR_HEIGHT + ACCEL_BAR_SEP); + + // Draw the 8-direction touchpad with center circle + int16_t tBarX = TFT_WIDTH - TOUCHBAR_WIDTH; + int16_t tBarY = barY + 4 + TOUCHBAR_HEIGHT / 2 + test->ibm_vga8.height + 15; + int16_t rSize = 35; + touchDrawCircle(&test->ibm_vga8, "Touch Pad", tBarX, tBarY, rSize, 8, true, test->touched, test->touchJoystick); + touchFillCircleSegments(tBarX, tBarY, rSize, 8, true); + + // Draw a dot indicating the analog touch spot + drawCircleFilled(tBarX + getCos1024(test->touchAngle) * test->touchRadius / 1024 * rSize / 1024, + tBarY - getSin1024(test->touchAngle) * test->touchRadius / 1024 * rSize / 1024, 3, + test->touched ? c500 : c333); + + // Plot some text depending on test status + char dbgStr[32] = {0}; + if (false == test->buttonsPassed) + { + sprintf(dbgStr, "Test Buttons"); + } + else if (false == test->touchPassed) + { + sprintf(dbgStr, "Test Touch Pad"); + } + else if (false == test->accelPassed) + { + sprintf(dbgStr, "Test Accelerometer"); + } + else if (false == test->bzrMicPassed) + { + sprintf(dbgStr, "Test Buzzer & Mic"); + } + else if (getTestModePassedSetting()) + { + sprintf(dbgStr, "All Tests Passed"); + } + + int16_t tWidth = textWidth(&test->ibm_vga8, dbgStr); + drawText(&test->ibm_vga8, c555, dbgStr, (TFT_WIDTH - tWidth) / 2, 0); + + sprintf(dbgStr, "Verify RGB LEDs & Tunes"); + tWidth = textWidth(&test->ibm_vga8, dbgStr); + drawText(&test->ibm_vga8, c555, dbgStr, 70, test->ibm_vga8.height + 8); + + // Animate a sprite + test->tSpriteElapsedUs += elapsedUs; + while (test->tSpriteElapsedUs >= 500000) + { + test->tSpriteElapsedUs -= 500000; + test->spriteFrame = (test->spriteFrame + 1) % 2; + } + + // Draw the sprite + if (0 == test->spriteFrame) + { + drawWsg(&test->kd_idle0, 32, 4, false, false, 0); + } + else + { + drawWsg(&test->kd_idle1, 32, 4, false, false, 0); + } + + // Pulse LEDs, each color for 1s + test->tLedElapsedUs += elapsedUs; + while (test->tLedElapsedUs >= (1000000 / 512)) + { + test->tLedElapsedUs -= (1000000 / 512); + switch (test->ledState) + { + case R_RISING: + { + test->cLed.r++; + if (255 == test->cLed.r) + { + test->ledState = R_FALLING; + } + break; + } + case R_FALLING: + { + test->cLed.r--; + if (0 == test->cLed.r) + { + test->ledState = G_RISING; + } + break; + } + case G_RISING: + { + test->cLed.g++; + if (255 == test->cLed.g) + { + test->ledState = G_FALLING; + } + break; + } + case G_FALLING: + { + test->cLed.g--; + if (0 == test->cLed.g) + { + test->ledState = B_RISING; + } + break; + } + case B_RISING: + { + test->cLed.b++; + if (255 == test->cLed.b) + { + test->ledState = B_FALLING; + } + break; + } + case B_FALLING: + { + test->cLed.b--; + if (0 == test->cLed.b) + { + test->ledState = R_RISING; + } + break; + } + } + } + + // Display LEDs + led_t leds[CONFIG_NUM_LEDS] = {0}; + for (uint8_t i = 0; i < CONFIG_NUM_LEDS; i++) + { + leds[i].r = test->cLed.r; + leds[i].g = test->cLed.g; + leds[i].b = test->cLed.b; + } + setLeds(leds, CONFIG_NUM_LEDS); +} + +/** + * @brief Helper function to draw button statuses to the TFT + * + * @param x + * @param y + * @param state + */ +void plotButtonState(int16_t x, int16_t y, testButtonState_t state) +{ + switch (state) + { + case BTN_NOT_PRESSED: + { + drawCircle(x, y, BTN_RADIUS, c500); + break; + } + case BTN_PRESSED: + { + drawCircleFilled(x, y, BTN_RADIUS, c005); + break; + } + case BTN_RELEASED: + { + drawCircleFilled(x, y, BTN_RADIUS, c050); + break; + } + } +} + +/** + * @brief Button callback, used to change settings + * + * @param evt The button event + */ +void testProcessButtons(buttonEvt_t* evt) +{ + // Find the button index based on bit + uint8_t btnIdx = 0; + switch (evt->button) + { + case PB_UP: + { + btnIdx = 0; + break; + } + case PB_DOWN: + { + btnIdx = 1; + break; + } + case PB_LEFT: + { + btnIdx = 2; + break; + } + case PB_RIGHT: + { + btnIdx = 3; + break; + } + case PB_A: + { + btnIdx = 4; + break; + } + case PB_B: + { + btnIdx = 5; + break; + } + case PB_START: + { + btnIdx = 6; + break; + } + case PB_SELECT: + { + btnIdx = 7; + break; + } + default: + { + return; + } + } + + // Transition states + if (evt->down) + { + if (BTN_NOT_PRESSED == test->buttonStates[btnIdx]) + { + test->buttonStates[btnIdx] = BTN_PRESSED; + } + } + else + { + if (BTN_PRESSED == test->buttonStates[btnIdx]) + { + test->buttonStates[btnIdx] = BTN_RELEASED; + } + } + + // Check if all buttons have passed + test->buttonsPassed = true; + for (uint8_t i = 0; i < 8; i++) + { + if (BTN_RELEASED != test->buttonStates[i]) + { + test->buttonsPassed = false; + } + } +} + +uint8_t touchJoystickToTouchIdx(touchJoystick_t touchJoystick) +{ + switch (touchJoystick) + { + case TB_CENTER: + return 0; + case TB_RIGHT: + return 1; + case TB_UP: + return 2; + case TB_LEFT: + return 3; + case TB_DOWN: + return 4; + case TB_UP_RIGHT: + return 5; + case TB_UP_LEFT: + return 6; + case TB_DOWN_LEFT: + return 7; + case TB_DOWN_RIGHT: + return 8; + default: + return UINT8_MAX; + } +} + +touchJoystick_t touchIdxToTouchJoystick(uint8_t touchIdx) +{ + switch (touchIdx) + { + case 0: + return TB_CENTER; + case 1: + return TB_RIGHT; + case 2: + return TB_UP; + case 3: + return TB_LEFT; + case 4: + return TB_DOWN; + case 5: + return TB_UP_RIGHT; + case 6: + return TB_UP_LEFT; + case 7: + return TB_DOWN_LEFT; + case 8: + return TB_DOWN_RIGHT; + default: + return UINT8_MAX; + } +} + +/** + * @brief Read, save, and validate touch + * + */ +void testReadAndValidateTouch(void) +{ + int32_t intensity; + test->touched = getTouchJoystick(&test->touchAngle, &test->touchRadius, &intensity); + + // Transition states + if (test->touched) + { + test->touchJoystick = getTouchJoystickZones(test->touchAngle, test->touchRadius, true, true); + uint8_t pad = touchJoystickToTouchIdx(test->touchJoystick); + if (pad == UINT8_MAX) + { + return; + } + + if (BTN_NOT_PRESSED == test->touchStates[pad]) + { + test->touchStates[pad] = BTN_PRESSED; + } + + if (test->lastTouchStateIdx != UINT8_MAX && pad != test->lastTouchStateIdx) + { + if (BTN_PRESSED == test->touchStates[test->lastTouchStateIdx]) + { + test->touchStates[test->lastTouchStateIdx] = BTN_RELEASED; + } + } + + test->lastTouchStateIdx = pad; + } + else if (test->lastTouchStateIdx != UINT8_MAX) + { + if (BTN_PRESSED == test->touchStates[test->lastTouchStateIdx]) + { + test->touchStates[test->lastTouchStateIdx] = BTN_RELEASED; + } + } + + // Check if all buttons have passed + test->touchPassed = true; + for (uint8_t i = 0; i < 9; i++) + { + if (BTN_RELEASED != test->touchStates[i]) + { + test->touchPassed = false; + } + } +} + +/** + * @brief Audio callback. Take the samples and pass them to test + * + * @param samples The samples to process + * @param sampleCnt The number of samples to process + */ +void testAudioCb(uint16_t* samples, uint32_t sampleCnt) +{ + // For each sample + for (uint32_t idx = 0; idx < sampleCnt; idx++) + { + // Push to test + PushSample32(&test->dd, samples[idx]); + + // If 128 samples have been pushed + test->samplesProcessed++; + if (test->samplesProcessed >= 128) + { + // Update LEDs + test->samplesProcessed = 0; + HandleFrameInfo(&test->end, &test->dd); + + // Keep track of max value for the spectrogram + int16_t maxVal = 0; + for (uint16_t i = 0; i < FIX_BINS; i++) + { + if (test->end.fuzzed_bins[i] > maxVal) + { + maxVal = test->end.fuzzed_bins[i]; + } + } + + // If already passed, just return + if (true == test->bzrMicPassed) + { + return; + } + } + } +} + +/** + * @brief Read, save, and validate accelerometer data + * + */ +void testReadAndValidateAccelerometer(void) +{ + // Read accel + int16_t x, y, z; + accelGetAccelVec(&x, &y, &z); + + // Save accel values + test->x = x; + test->y = y; + test->z = z; + + // Make sure all values are nonzero + if ((x != 0) && (y != 0) && (z != 0)) + { + // Make sure some value is shook + if ((x > 500) || (y > 500) || (z > 500)) + { + // Pass! + test->accelPassed = true; + } + } +} + +static void touchFillCircleSegments(int16_t x, int16_t y, int16_t r, int16_t segs, bool center) +{ + uint8_t numTouchElem = (sizeof(test->touchStates) / sizeof(test->touchStates[0])); + for (uint8_t touchIdx = 0; touchIdx < numTouchElem; touchIdx++) + { + paletteColor_t color; + switch (test->touchStates[touchIdx]) + { + case BTN_PRESSED: + { + color = c005; + break; + } + case BTN_RELEASED: + { + color = c050; + break; + } + case BTN_NOT_PRESSED: + default: + { + color = cTransparent; + } + } + + int16_t angle = 0; + int16_t fillR = r / 2; + switch (touchIdxToTouchJoystick(touchIdx)) + { + case TB_CENTER: + angle = 0; + fillR = 0; + break; + + case TB_RIGHT: + angle = 0; + break; + + case TB_UP | TB_RIGHT: + angle = 45; + break; + + case TB_UP: + angle = 90; + break; + + case TB_UP | TB_LEFT: + angle = 135; + break; + + case TB_LEFT: + angle = 180; + break; + + case TB_DOWN | TB_LEFT: + angle = 225; + break; + + case TB_DOWN: + angle = 270; + break; + + case TB_DOWN | TB_RIGHT: + angle = 315; + break; + } + + if (color != cTransparent) + { + // Fill in the segment + floodFill(x + getCos1024(angle) * fillR / 1024, y - getSin1024(angle) * fillR / 1024, color, x - r - 1, + y - r - 1, x + r + 1, y + r + 1); + } + } +} diff --git a/main/modes/factoryTest/factoryTest.h b/main/modes/factoryTest/factoryTest.h new file mode 100644 index 000000000..6b0f65577 --- /dev/null +++ b/main/modes/factoryTest/factoryTest.h @@ -0,0 +1,8 @@ +#ifndef _FACTORYTEST_H_ +#define _FACTORYTEST_H_ + +#include "swadge2024.h" + +extern swadgeMode_t factoryTestMode; + +#endif diff --git a/main/modes/mainMenu/mainMenu.c b/main/modes/mainMenu/mainMenu.c index 9dc420ee0..e811f6062 100644 --- a/main/modes/mainMenu/mainMenu.c +++ b/main/modes/mainMenu/mainMenu.c @@ -11,6 +11,7 @@ #include "colorchord.h" #include "dance.h" #include "demoMode.h" +#include "factoryTest.h" #include "gamepad.h" #include "jukebox.h" #include "lumberjack.h" @@ -146,6 +147,7 @@ static void mainMenuEnterMode(void) addSingleItemToMenu(mainMenu->menu, accelTestMode.modeName); addSingleItemToMenu(mainMenu->menu, demoMode.modeName); addSingleItemToMenu(mainMenu->menu, touchTestMode.modeName); + addSingleItemToMenu(mainMenu->menu, factoryTestMode.modeName); mainMenu->menu = endSubMenu(mainMenu->menu); // Start a submenu for settings @@ -242,6 +244,10 @@ static void mainMenuCb(const char* label, bool selected, uint32_t settingVal) { switchToSwadgeMode(&demoMode); } + else if (label == factoryTestMode.modeName) + { + switchToSwadgeMode(&factoryTestMode); + } else if (label == gamepadMode.modeName) { switchToSwadgeMode(&gamepadMode); diff --git a/main/modes/touchTest/touchTest.c b/main/modes/touchTest/touchTest.c index f667a66c6..08137df27 100644 --- a/main/modes/touchTest/touchTest.c +++ b/main/modes/touchTest/touchTest.c @@ -52,8 +52,6 @@ static void touchTestReset(void); static void touchTestHandleInput(void); static void touchTestBackgroundDrawCallback(int16_t x, int16_t y, int16_t w, int16_t h, int16_t up, int16_t upNum); -static void touchDrawCircle(const char* label, int16_t x, int16_t y, int16_t r, int16_t segs, bool center, - touchJoystick_t val); static void touchDrawVector(int16_t x, int16_t y, int16_t r); static void touchTestDraw(void); @@ -201,18 +199,20 @@ static void touchTestBackgroundDrawCallback(int16_t x, int16_t y, int16_t w, int } /** - * @brief + * @brief Draw a circular representation of the touchpad * - * @param x - * @param y - * @param r - * @param segs + * @param x X coordinate on the TFT for the center of the circle + * @param y Y coordinate on the TFT for the center of the circle + * @param r Radius of the circle + * @param segs Number of directional segments to split the touchpad into + * @param center True to include a center zone, false to only use directional segments + * @param touched Whether the touchpad is currently being touched + * @param val The output you received from a prior call to `getTouchJoystickZones` */ -static void touchDrawCircle(const char* label, int16_t x, int16_t y, int16_t r, int16_t segs, bool center, - touchJoystick_t val) +void touchDrawCircle(font_t* font, const char* label, int16_t x, int16_t y, int16_t r, int16_t segs, bool center, + bool touched, touchJoystick_t val) { - drawText(&touchTest->ibm, c555, label, x - textWidth(&touchTest->ibm, label) / 2, - y - r - touchTest->ibm.height - 5); + drawText(font, c555, label, x - textWidth(font, label) / 2, y - r - font->height - 5); // Draw outer circle drawCircle(x, y, r, c222); @@ -234,7 +234,7 @@ static void touchDrawCircle(const char* label, int16_t x, int16_t y, int16_t r, drawCircle(x, y, centerR - 1, c222); } - if (touchTest->touch) + if (touched) { int16_t angle = 0; int16_t fillR = r / 2; @@ -404,18 +404,18 @@ static void touchTestDraw(void) } // Draw the 4-direction touchpad circle - touchDrawCircle("4", TFT_WIDTH / 2, TFT_HEIGHT / 4, 35, 4, false, + touchDrawCircle(&touchTest->ibm, "4", TFT_WIDTH / 2, TFT_HEIGHT / 4, 35, 4, false, touchTest->touch, getTouchJoystickZones(touchTest->angle, touchTest->radius, false, false)); // Draw the 8-direction touchpad circle - touchDrawCircle("8", TFT_WIDTH - 60, TFT_HEIGHT / 4, 35, 8, false, + touchDrawCircle(&touchTest->ibm, "8", TFT_WIDTH - 60, TFT_HEIGHT / 4, 35, 8, false, touchTest->touch, getTouchJoystickZones(touchTest->angle, touchTest->radius, false, true)); // Draw the 4-direction touchpad with center circle - touchDrawCircle("4+Center", TFT_WIDTH / 2, TFT_HEIGHT - TFT_HEIGHT / 4, 35, 4, true, - getTouchJoystickZones(touchTest->angle, touchTest->radius, true, false)); + touchDrawCircle(&touchTest->ibm, "4+Center", TFT_WIDTH / 2, TFT_HEIGHT - TFT_HEIGHT / 4, 35, 4, true, + touchTest->touch, getTouchJoystickZones(touchTest->angle, touchTest->radius, true, false)); // Draw the 8-direction touchpad with center circle - touchDrawCircle("8+Center", TFT_WIDTH - 60, TFT_HEIGHT - TFT_HEIGHT / 4, 35, 8, true, - getTouchJoystickZones(touchTest->angle, touchTest->radius, true, true)); + touchDrawCircle(&touchTest->ibm, "8+Center", TFT_WIDTH - 60, TFT_HEIGHT - TFT_HEIGHT / 4, 35, 8, true, + touchTest->touch, getTouchJoystickZones(touchTest->angle, touchTest->radius, true, true)); } diff --git a/main/modes/touchTest/touchTest.h b/main/modes/touchTest/touchTest.h index 0619564ea..8aa94056f 100644 --- a/main/modes/touchTest/touchTest.h +++ b/main/modes/touchTest/touchTest.h @@ -5,4 +5,7 @@ extern swadgeMode_t touchTestMode; +void touchDrawCircle(font_t* font, const char* label, int16_t x, int16_t y, int16_t r, int16_t segs, bool center, + bool touched, touchJoystick_t val); + #endif \ No newline at end of file diff --git a/main/swadge2024.c b/main/swadge2024.c index ff9b595c2..f17d7c3fc 100644 --- a/main/swadge2024.c +++ b/main/swadge2024.c @@ -140,11 +140,13 @@ #include #include "advanced_usb_control.h" +#include "shapes.h" #include "swadge2024.h" -#include "mainMenu.h" + +#include "factoryTest.h" #include "lumberjack.h" +#include "mainMenu.h" #include "quickSettings.h" -#include "shapes.h" //============================================================================== // Defines @@ -203,8 +205,20 @@ void app_main(void) // Init NVS. Do this first to get test mode status and crashwrap logs initNvs(true); - // Assume the main menu is shown - cSwadgeMode = &mainMenuMode; + // Read settings from NVS + readAllSettings(); + + // If test mode was passed + if (getTestModePassedSetting()) + { + // Show the main menu + cSwadgeMode = &mainMenuMode; + } + else + { + // Otherwise enter test mode + cSwadgeMode = &factoryTestMode; + } // If the ESP woke from sleep, and there is a pending Swadge Mode if ((ESP_SLEEP_WAKEUP_TIMER == esp_sleep_get_wakeup_cause()) && (NULL != pendingSwadgeMode)) @@ -307,9 +321,6 @@ void app_main(void) static int64_t tLastLoopUs = 0; tLastLoopUs = esp_timer_get_time(); - // Read settings from NVS - readAllSettings(); - // Initialize the swadge mode if (NULL != cSwadgeMode->fnEnterMode) { diff --git a/main/utils/settingsManager.c b/main/utils/settingsManager.c index fce90768c..89ae4da06 100644 --- a/main/utils/settingsManager.c +++ b/main/utils/settingsManager.c @@ -51,11 +51,11 @@ typedef struct //============================================================================== DECL_SETTING(test, 0, 1, 0); -DECL_SETTING(bgm, 0, 13, 13); -DECL_SETTING(sfx, 0, 13, 13); -DECL_SETTING(tft_br, 0, 7, 7); -DECL_SETTING(led_br, 0, 8, 5); -DECL_SETTING(mic, 0, 7, 7); +DECL_SETTING(bgm, 0, MAX_VOLUME, MAX_VOLUME); +DECL_SETTING(sfx, 0, MAX_VOLUME, MAX_VOLUME); +DECL_SETTING(tft_br, 0, MAX_TFT_BRIGHTNESS, MAX_TFT_BRIGHTNESS); +DECL_SETTING(led_br, 0, MAX_LED_BRIGHTNESS, 5); +DECL_SETTING(mic, 0, MAX_MIC_GAIN, MAX_MIC_GAIN); DECL_SETTING(cc_mode, ALL_SAME_LEDS, LINEAR_LEDS, ALL_SAME_LEDS); DECL_SETTING(scrn_sv, 0, 300, 20); @@ -189,7 +189,7 @@ const settingParam_t* getBgmVolumeSettingBounds(void) /** * @brief Set the current background music volume setting. This calls bzrSetBgmVolume() after writing to NVS. * - * @param vol The new volume setting + * @param vol The new volume setting, 0 to MAX_VOLUME * @return true if the setting was written, false if it wasn't */ bool setBgmVolumeSetting(uint16_t vol) @@ -227,7 +227,7 @@ const settingParam_t* getSfxVolumeSettingBounds(void) /** * @brief Set the current sound effects volume setting. This calls bzrSetSfxVolume() after writing to NVS. * - * @param vol The new volume setting + * @param vol The new volume setting, 0 to MAX_VOLUME * @return true if the setting was written, false if it wasn't */ bool setSfxVolumeSetting(uint16_t vol) @@ -265,7 +265,7 @@ const settingParam_t* getTftBrightnessSettingBounds(void) /** * @brief Set the current TFT brightness setting. This calls setTFTBacklightBrightness() after writing to NVS. * - * @param newVal the new TFT brightness setting + * @param newVal the new TFT brightness setting, 0 to MAX_TFT_BRIGHTNESS * @return true if the setting was written, false if it wasn't */ bool setTftBrightnessSetting(uint8_t newVal) @@ -303,7 +303,7 @@ const settingParam_t* getLedBrightnessSettingBounds(void) /** * @brief Set the current LED brightness setting. This calls setLedBrightness() after writing to NVS. * - * @param brightness The new LED brightness setting + * @param brightness The new LED brightness setting, 0 to MAX_LED_BRIGHTNESS * @return true if the setting was written, false if it was not */ bool setLedBrightnessSetting(uint8_t brightness) @@ -371,7 +371,7 @@ const settingParam_t* getMicGainSettingBounds(void) /** * @brief Set the current microphone gain setting. The new value is immediately used when sampling the microphone. * - * @param newGain The new microphone gain setting + * @param newGain The new microphone gain setting, 0 to MAX_MIC_GAIN * @return true if the setting was written, false if it wasn't */ bool setMicGainSetting(uint8_t newGain) diff --git a/tools/spiffs_file_preprocessor/src/fileUtils.h b/tools/spiffs_file_preprocessor/src/fileUtils.h index 7199b032d..f179d5745 100644 --- a/tools/spiffs_file_preprocessor/src/fileUtils.h +++ b/tools/spiffs_file_preprocessor/src/fileUtils.h @@ -4,9 +4,9 @@ #include #define HI_WORD(x) ((x >> 16) & 0xFFFF) -#define LO_WORD(x) ((x)&0xFFFF) +#define LO_WORD(x) ((x) & 0xFFFF) #define HI_BYTE(x) ((x >> 8) & 0xFF) -#define LO_BYTE(x) ((x)&0xFF) +#define LO_BYTE(x) ((x) & 0xFF) long getFileSize(const char* fname); bool doesFileExist(const char* fname); diff --git a/tools/spiffs_file_preprocessor/src/midi_processor.c b/tools/spiffs_file_preprocessor/src/midi_processor.c index cd487d223..018f9d574 100644 --- a/tools/spiffs_file_preprocessor/src/midi_processor.c +++ b/tools/spiffs_file_preprocessor/src/midi_processor.c @@ -247,6 +247,8 @@ void process_midi(const char* inFile, const char* outDir) /* For each note */ unsigned long int lastNoteStart = track->notes[0].timeBeforeAppear; + unsigned long int introRest = lastNoteStart; + for (int midiNoteIdx = 0; midiNoteIdx < track->nbOfNotes; midiNoteIdx++) { /* Get a reference to this note */ @@ -255,6 +257,20 @@ void process_midi(const char* inFile, const char* outDir) /* Before processing the note, check for events*/ checkMidiEvents(midiParser, lastNoteStart, note->timeBeforeAppear, ¶ms); + /* If this song should start with a rest */ + if (0 != introRest) + { + /* Save the rest */ + notes[trackIdx][noteIdxs[trackIdx]].note = SILENCE; + notes[trackIdx][noteIdxs[trackIdx]].timeMs + = (params.tempo * introRest) / (1000 * midiParser->ticks); + totalLength[trackIdx] += notes[trackIdx][noteIdxs[trackIdx]].timeMs; + noteIdxs[trackIdx]++; + + /* Don't do this again */ + introRest = 0; + } + /* Get a reference to the next note, if it exists */ Note* nextNote = NULL; if ((midiNoteIdx + 1) < track->nbOfNotes)