// ============================================================ // Board Check — Open Chess // // The self-test. Sweeps every LED through white, red, green, and blue, then // asks you to put a piece on every square so each of the 64 sensors is // confirmed. Both buttons, the buzzer, and the battery gauge are exercised // too, and the whole thing is mirrored on the Connect page. // // Button A run the LED sweep again // Button B restart the sensor test // // Everything hardware-related — sensors, LEDs, battery, the USB link to the // Connect page — is handled by OpenChessCore. This file is only the test. // ============================================================ #include using namespace oc; // ── Things you might want to change ───────────────────────────────────────── const Color WAITING_COLOR = Color(0, 0, 40); // square not yet confirmed const Color CONFIRMED_COLOR = GREEN; // sensor saw a piece here const Color PRESENT_COLOR = WHITE; // a piece is on it right now const uint16_t SWEEP_STEP_MS = 12; // per pixel in the chase // ── Test state ────────────────────────────────────────────────────────────── bool confirmed[64]; int confirmedCount = 0; bool allDone = false; void setup() { begin("board-check", "Board Check", "2.0.0"); soundCheck(); ledSweep(); resetSensorTest(); } void loop() { service(); if (buttons.pressed(BUTTON_A)) { anim::flashAll(GREEN, 1, 120, 80); app.log("Button A OK"); ledSweep(); drawSensorTest(); } if (buttons.pressed(BUTTON_B)) { anim::flashAll(BLUE, 1, 120, 80); app.log("Button B OK"); resetSensorTest(); } // Any square that has ever seen a piece counts as confirmed. bool changed = false; for (int i = 0; i < 64; i++) { if (board.occupied(i) && !confirmed[i]) { confirmed[i] = true; confirmedCount++; changed = true; } } if (changed || board.changedSinceSync()) { drawSensorTest(); board.sync(); reportProgress(); } if (confirmedCount == 64 && !allDone) { allDone = true; sound.win(); app.status("over", "All 64 sensors confirmed. Everything works."); app.log("PASS: 64/64 sensors, 64 LEDs, both buttons available"); celebrate(); } } // ── Buzzer ────────────────────────────────────────────────────────────────── // Every effect in the library, in order, so a dead buzzer or a wrong pin is // obvious before the LED and sensor tests start. Each one is named on the // Connect page as it plays. void soundCheck() { app.status("test", "Sound test: listen for each effect"); struct Effect { Sfx effect; const char *name; }; const Effect effects[] = { {Sfx::PickUp, "pick up"}, {Sfx::Place, "place"}, {Sfx::ReturnSame, "put back"}, {Sfx::Move, "move"}, {Sfx::Capture, "capture"}, {Sfx::Castle, "castle"}, {Sfx::Promote, "promote"}, {Sfx::Check, "check"}, {Sfx::Checkmate, "checkmate"}, {Sfx::Draw, "draw"}, {Sfx::Illegal, "illegal"}, {Sfx::Score, "score"}, {Sfx::Bounce, "bounce"}, {Sfx::Alert, "alert"}, }; for (const Effect &e : effects) { app.log("Sound: %s", e.name); sound.play(e.effect); wait(700); } // A slow rising sweep: catches a buzzer that only resonates at one pitch. for (uint16_t hz = 300; hz <= 2000; hz += 100) { sound.beep(hz, 45); wait(55); } app.log("Sound test complete"); } // ── LED sweep ─────────────────────────────────────────────────────────────── // Whole-board colours first (spots a wrong colour order or a dead channel), // then a single-pixel chase (spots a dead or stuck pixel). void ledSweep() { app.status("test", "LED test: white, red, green, blue"); const Color colors[4] = { WHITE, RED, GREEN, BLUE }; for (int c = 0; c < 4; c++) { lights.fill(colors[c]); lights.show(); wait(450); } lights.clear(); lights.show(); app.status("test", "LED test: one pixel at a time"); for (int i = 0; i < 64; i++) { lights.clear(); lights.set(i, WHITE); lights.show(); wait(SWEEP_STEP_MS); } lights.clear(); lights.show(); // The core showed the battery gauge at boot; show it again here so the // battery reading is part of every test run. if (power.present()) { app.log("Battery %d%% (%.2f V)%s", power.percent(), power.volts(), power.charging() ? ", charging" : power.usb() ? ", on USB" : ""); anim::gauge(power.percent()); } else { app.log("No charger IC detected - battery test skipped"); } } // ── Sensor test ───────────────────────────────────────────────────────────── void resetSensorTest() { for (int i = 0; i < 64; i++) confirmed[i] = false; confirmedCount = 0; allDone = false; board.sync(); drawSensorTest(); app.status("test", "Sensor test: put a piece on every square (0/64)"); app.log("Sensor test started. Place a piece on each square."); } void drawSensorTest() { for (int i = 0; i < 64; i++) { lights.set(i, board.occupied(i) ? PRESENT_COLOR : confirmed[i] ? CONFIRMED_COLOR : WAITING_COLOR); } lights.show(); } void reportProgress() { if (allDone) return; char text[64]; snprintf(text, sizeof(text), "Sensor test: put a piece on every square (%d/64)", confirmedCount); app.status("test", text); } // Rainbow sweep, then hold green until a button restarts something. void celebrate() { const Color rainbow[6] = { RED, ORANGE, GOLD, GREEN, CYAN, PURPLE }; for (int pass = 0; pass < 3; pass++) { for (int d = 0; d < 15; d++) { for (int row = 0; row < 8; row++) for (int col = 0; col < 8; col++) lights.set(row, col, rainbow[(row + col + d) % 6]); lights.show(); wait(40); } } lights.fill(CONFIRMED_COLOR); lights.show(); }