/* Program title: LEDaddresstestv2 Written by Jeff Ritchie with assistance from OpenAI Codex Date: May 27, 2026 LED address test version 2: Interactive NeoPixel slat address confirmation program for the 16-slat display used in Version 21. This sketch does not use the VL53L5CX sensor and does not change any existing programs. For each slat, the program lights the first mounted LED red and the last mounted LED green. The Serial Monitor asks whether the red start marker or green end marker is correct, short, or long, and by how many LEDs. The sketch adjusts the raw NeoPixel addresses for you. Serial Monitor commands: 0 Marker is correct. s2 Marker is short by 2 LEDs and should move farther outward. short 2 Same as above. l1 Marker is long by 1 LED and should move inward. long 1 Same as above. r Replay the current slat. b Go back one slat. Open the Serial Monitor at 115200 baud and set the line ending to Newline. At the end, the sketch prints a corrected address map that can be copied into the motion program after the physical addresses are confirmed. */ #include // XIAO ESP32C3 D7 drives NeoPixel data in the Version 21 program. #define NEOPIXEL_PIN D7 const int NUM_SLATS = 16; const int PIXELS_PER_SLAT = 56; const int NUMPIXELS = 972; const int FIRST_LED_HEIGHT = 0; const int LAST_LED_HEIGHT = PIXELS_PER_SLAT - 1; // Keep the test low power: only two bright LEDs are normally lit. const int DISPLAY_BRIGHTNESS = 35; const int MARKER_BRIGHTNESS = 120; // Starting guesses copied from Version 21. int confirmedFirstAddress[NUM_SLATS] = { 0, 116, 123, 240, 241, 358, 367, 484, 485, 602, 609, 728, 729, 847, 854, 971 }; int confirmedLastAddress[NUM_SLATS] = { 55, 61, 178, 185, 296, 303, 422, 429, 540, 547, 664, 673, 784, 792, 909, 916 }; Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXEL_PIN, NEO_RGB + NEO_KHZ800); int currentSlat = 0; bool finished = false; enum PromptStage { ASK_START_MARKER, ASK_END_MARKER, ASK_FINAL_CONFIRM }; PromptStage promptStage = ASK_START_MARKER; void showCurrentSlat(); void showSlatEndpoints(int slat); void promptForCurrentSlat(); void handleSerialLine(String line); bool parseCorrection(String line, bool &isShort, int &amount); void applyEndpointCorrection(bool correctingStart, bool isShort, int amount); bool addressIsValid(int address); void printFinalMap(); void printSlatAddressLine(int slat); uint32_t redLevel(int brightness); uint32_t greenLevel(int brightness); void setup() { Serial.begin(115200); delay(1000); pixels.begin(); pixels.setBrightness(DISPLAY_BRIGHTNESS); pixels.clear(); pixels.show(); Serial.println(); Serial.println("LEDaddresstestv2"); Serial.println("Interactive NeoPixel slat address confirmation."); Serial.println(); Serial.println("For each slat:"); Serial.println(" Red = first mounted LED in the slat"); Serial.println(" Green = last mounted LED in the slat"); Serial.println(); Serial.println("For each marker, type 0 if correct, s2 if short by 2, or l1 if long by 1."); Serial.println("Use r to replay the current slat or b to go back."); Serial.println("Slats are numbered 0 through 15."); Serial.println(); showCurrentSlat(); } void loop() { if (finished) { return; } if (Serial.available() > 0) { String line = Serial.readStringUntil('\n'); line.trim(); if (line.length() > 0) { handleSerialLine(line); } } } void showCurrentSlat() { showSlatEndpoints(currentSlat); promptForCurrentSlat(); } void showSlatEndpoints(int slat) { pixels.clear(); int firstAddress = confirmedFirstAddress[slat]; int lastAddress = confirmedLastAddress[slat]; if (addressIsValid(firstAddress)) { pixels.setPixelColor(firstAddress, redLevel(MARKER_BRIGHTNESS)); } if (addressIsValid(lastAddress)) { pixels.setPixelColor(lastAddress, greenLevel(MARKER_BRIGHTNESS)); } pixels.show(); } void promptForCurrentSlat() { Serial.println(); Serial.print("SLAT "); Serial.print(currentSlat); Serial.print(" of "); Serial.println(NUM_SLATS - 1); printSlatAddressLine(currentSlat); if (promptStage == ASK_START_MARKER) { Serial.println("RED START marker: correct, short, or long?"); Serial.println("Enter 0, s[number], short [number], l[number], or long [number]:"); } else if (promptStage == ASK_END_MARKER) { Serial.println("GREEN END marker: correct, short, or long?"); Serial.println("Enter 0, s[number], short [number], l[number], or long [number]:"); } else { Serial.println("Confirm this slat?"); Serial.println("Enter y to confirm, r to replay, or b to go back:"); } } void handleSerialLine(String line) { line.toLowerCase(); if (line == "r" || line == "replay") { showCurrentSlat(); return; } if (line == "b" || line == "back") { if (currentSlat > 0) { currentSlat--; } promptStage = ASK_START_MARKER; showCurrentSlat(); return; } if (promptStage == ASK_FINAL_CONFIRM) { if (line == "y" || line == "yes" || line == "0") { Serial.print("Confirmed slat "); Serial.println(currentSlat); currentSlat++; if (currentSlat >= NUM_SLATS) { finished = true; pixels.clear(); pixels.show(); printFinalMap(); } else { promptStage = ASK_START_MARKER; showCurrentSlat(); } return; } Serial.println("Use y to confirm this slat, r to replay, or b to go back."); promptForCurrentSlat(); return; } bool isShort; int amount; if (!parseCorrection(line, isShort, amount)) { Serial.println("I did not understand that entry."); Serial.println("Use 0, s2, short 2, l1, long 1, r, or b."); promptForCurrentSlat(); return; } if (amount > 0) { applyEndpointCorrection(promptStage == ASK_START_MARKER, isShort, amount); } if (promptStage == ASK_START_MARKER) { promptStage = ASK_END_MARKER; } else { promptStage = ASK_FINAL_CONFIRM; } showCurrentSlat(); } bool parseCorrection(String line, bool &isShort, int &amount) { line.replace(',', ' '); line.trim(); if (line == "0" || line == "ok" || line == "correct" || line == "y" || line == "yes") { isShort = true; amount = 0; return true; } if (line.startsWith("short")) { isShort = true; line.remove(0, 5); } else if (line.startsWith("long")) { isShort = false; line.remove(0, 4); } else if (line.startsWith("s")) { isShort = true; line.remove(0, 1); } else if (line.startsWith("l")) { isShort = false; line.remove(0, 1); } else { return false; } line.trim(); amount = line.toInt(); if (amount <= 0) { return false; } return true; } void applyEndpointCorrection(bool correctingStart, bool isShort, int amount) { int firstAddress = confirmedFirstAddress[currentSlat]; int lastAddress = confirmedLastAddress[currentSlat]; int correctedAddress; int outwardDirection; if (correctingStart) { outwardDirection = firstAddress < lastAddress ? -1 : 1; correctedAddress = firstAddress + (isShort ? outwardDirection : -outwardDirection) * amount; confirmedFirstAddress[currentSlat] = constrain(correctedAddress, 0, NUMPIXELS - 1); } else { outwardDirection = lastAddress < firstAddress ? -1 : 1; correctedAddress = lastAddress + (isShort ? outwardDirection : -outwardDirection) * amount; confirmedLastAddress[currentSlat] = constrain(correctedAddress, 0, NUMPIXELS - 1); } Serial.print(correctingStart ? "Updated red/start marker " : "Updated green/end marker "); Serial.print(isShort ? "outward by " : "inward by "); Serial.print(amount); Serial.println(" LEDs."); } bool addressIsValid(int address) { return address >= 0 && address < NUMPIXELS; } void printFinalMap() { Serial.println(); Serial.println("All slats confirmed."); Serial.println("Corrected endpoint list:"); for (int slat = 0; slat < NUM_SLATS; slat++) { printSlatAddressLine(slat); } Serial.println(); Serial.println("Derived Version 21-style map:"); Serial.println("const int SLAT_START_ADDRESS[NUM_SLATS] = {"); Serial.print(" "); for (int slat = 0; slat < NUM_SLATS; slat++) { int startAddress = min(confirmedFirstAddress[slat], confirmedLastAddress[slat]); Serial.print(startAddress); if (slat < NUM_SLATS - 1) { Serial.print(", "); } if ((slat + 1) % 4 == 0 && slat < NUM_SLATS - 1) { Serial.println(); Serial.print(" "); } } Serial.println(); Serial.println("};"); Serial.println(); Serial.println("const bool SLAT_RUNS_UP[NUM_SLATS] = {"); Serial.print(" "); for (int slat = 0; slat < NUM_SLATS; slat++) { bool runsUp = confirmedLastAddress[slat] > confirmedFirstAddress[slat]; Serial.print(runsUp ? "true" : "false"); if (slat < NUM_SLATS - 1) { Serial.print(", "); } if ((slat + 1) % 4 == 0 && slat < NUM_SLATS - 1) { Serial.println(); Serial.print(" "); } } Serial.println(); Serial.println("};"); Serial.println(); Serial.println("Reset the board to run the confirmation again."); } void printSlatAddressLine(int slat) { Serial.print("Slat "); Serial.print(slat); Serial.print(": first/raw red = "); Serial.print(confirmedFirstAddress[slat]); Serial.print(", last/raw green = "); Serial.println(confirmedLastAddress[slat]); } uint32_t redLevel(int brightness) { // Copied from Version 21: on this installation, Color(0, brightness, 0) // produces red. return pixels.Color(0, brightness, 0); } uint32_t greenLevel(int brightness) { return pixels.Color(brightness, 0, 0); }