From 47c17dba364ad58c728d7045c3647655ea177b69 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Thu, 5 Mar 2026 20:25:57 +1300 Subject: [PATCH] Add mpremote tests for scaling, roll, and point Made-with: Cursor --- pico/test/test_fill_n.py | 52 ++++++++++++++++ pico/test/test_point.py | 130 +++++++++++++++++++++++++++++++++++++++ pico/test/test_roll.py | 118 +++++++++++++++++++++++++++++++++++ pico/test/test_scale.py | 100 ++++++++++++++++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 pico/test/test_fill_n.py create mode 100644 pico/test/test_point.py create mode 100644 pico/test/test_roll.py create mode 100644 pico/test/test_scale.py diff --git a/pico/test/test_fill_n.py b/pico/test/test_fill_n.py new file mode 100644 index 0000000..cb1b2b6 --- /dev/null +++ b/pico/test/test_fill_n.py @@ -0,0 +1,52 @@ +""" +On-device test for Presets.fill_n() using mpremote. + +Usage (from pico/ dir or project root with adjusted paths): + + mpremote connect cp src/*.py : + mpremote connect cp src/patterns/*.py :patterns + mpremote connect cp lib/*.py : + mpremote connect cp test/test_fill_n.py : + mpremote connect run test_fill_n.py + +This script: + - Instantiates Presets + - Calls fill_n() with a simple range + - Lets you visually confirm that all strips show the same proportional segment + and that equal-length strip pairs have identical lit indices. +""" + +from presets import Presets + + +def main(): + presets = Presets() + presets.load() + + # Choose a simple test range on the reference strip (strip 0). + ref_len = presets.strip_length(0) + if ref_len <= 0: + print("No strips or invalid length; aborting fill_n test.") + return + + # Use a central segment so it's easy to see. + start = ref_len // 4 + end = 3 * ref_len // 4 + print("Running fill_n test from", start, "to", end, "on reference strip 0.") + + color = (0, 50, 0) # dim green + + # First, clear everything + for strip in presets.strips: + strip.fill((0, 0, 0)) + strip.show() + + # Apply fill_n, which will use scale() internally. + presets.fill_n(color, start, end) + + print("fill_n test applied; visually inspect strips.") + + +if __name__ == "__main__": + main() + diff --git a/pico/test/test_point.py b/pico/test/test_point.py new file mode 100644 index 0000000..a793f1b --- /dev/null +++ b/pico/test/test_point.py @@ -0,0 +1,130 @@ +""" +On-device test for the Point pattern using mpremote. + +Usage (from pico/ dir or project root with adjusted paths): + + mpremote connect cp src/*.py : + mpremote connect cp src/patterns/*.py :patterns + mpremote connect cp lib/*.py : + mpremote connect cp test/test_point.py : + mpremote connect run test_point.py + +This script: + - Instantiates Presets + - Creates a few in-memory 'point' presets with different ranges/colors + - Selects each one so you can visually confirm the segments +""" + +from presets import Presets, Preset + + +def make_point_preset(name, colors, n_values, brightness=255): + """ + Helper to build a Preset for the 'point' pattern. + + colors: list of up to 4 (r,g,b) tuples + n_values: list/tuple of 8 ints [n1..n8] + """ + # Pad or trim colors to 4 entries + cs = list(colors)[:4] + while len(cs) < 4: + cs.append((0, 0, 0)) + + n1, n2, n3, n4, n5, n6, n7, n8 = n_values + data = { + "p": "point", + "c": cs, + "b": brightness, + "n1": n1, + "n2": n2, + "n3": n3, + "n4": n4, + "n5": n5, + "n6": n6, + "n7": n7, + "n8": n8, + # 'a' is not used by point; it's static + } + return name, Preset(data) + + +def show_and_wait(presets, name, preset_obj, wait_ms): + """Select a static 'point' preset and hold it for wait_ms.""" + presets.presets[name] = preset_obj + presets.select(name) + # Point draws immediately in run(), then just yields; one tick is enough. + presets.tick() + + import utime + + start = utime.ticks_ms() + while utime.ticks_diff(utime.ticks_ms(), start) < wait_ms: + # Keep ticking in case other logic ever depends on it + presets.tick() + + +def main(): + presets = Presets() + presets.load() + + num_leds = presets.strip_length(0) + if num_leds <= 0: + print("No strips; aborting point test.") + return + + print("Starting point pattern test...") + + quarter = num_leds // 4 + half = num_leds // 2 + + point_presets = [] + + # 1. Single band: first quarter, red + point_presets.append( + make_point_preset( + "point_red_q1", + colors=[(255, 0, 0)], + n_values=[0, quarter - 1, 0, -1, 0, -1, 0, -1], + ) + ) + + # 2. Two bands: red first half, green second half + point_presets.append( + make_point_preset( + "point_red_green_halves", + colors=[(255, 0, 0), (0, 255, 0)], + n_values=[0, half - 1, half, num_leds - 1, 0, -1, 0, -1], + ) + ) + + # 3. Three bands: R, G, B quarters + point_presets.append( + make_point_preset( + "point_rgb_quarters", + colors=[(255, 0, 0), (0, 255, 0), (0, 0, 255)], + n_values=[ + 0, + quarter - 1, # red + quarter, + 2 * quarter - 1, # green + 2 * quarter, + 3 * quarter - 1, # blue + 0, + -1, + ], + ) + ) + + # Show each for ~4 seconds + for name, preset_obj in point_presets: + print("Showing point preset:", name) + show_and_wait(presets, name, preset_obj, wait_ms=4000) + + print("Point pattern test finished. Turning off LEDs.") + presets.select("off") + presets.tick() + + +if __name__ == "__main__": + main() + diff --git a/pico/test/test_roll.py b/pico/test/test_roll.py new file mode 100644 index 0000000..486e2da --- /dev/null +++ b/pico/test/test_roll.py @@ -0,0 +1,118 @@ +""" +On-device test for the Roll pattern using mpremote. + +Usage (from pico/ dir or project root with adjusted paths): + + mpremote connect cp src/*.py : + mpremote connect cp src/patterns/*.py :patterns + mpremote connect cp lib/*.py : + mpremote connect cp test/test_roll.py : + mpremote connect run test_roll.py + +This script: + - Instantiates Presets + - Creates a few in-memory roll presets with different parameters + - Runs each one for a short time so you can visually compare behaviour +""" + +import utime +from presets import Presets, Preset + + +def make_roll_preset(name, color1, color2, n1=0, n2=0, n3=0, n4=0, delay_ms=50, brightness=255): + """Helper to build a Preset dict for the roll pattern.""" + data = { + "p": "roll", + "c": [color1, color2], + "b": brightness, + "d": delay_ms, + "n1": n1, + "n2": n2, + "n3": n3, + "n4": n4, + "a": True, # animated + } + return name, Preset(data) + + +def run_preset(presets, name, preset_obj, duration_ms): + """Run a given roll preset for duration_ms using the existing tick loop.""" + presets.presets[name] = preset_obj + presets.select(name) + + start = utime.ticks_ms() + while utime.ticks_diff(utime.ticks_ms(), start) < duration_ms: + presets.tick() + + +def main(): + presets = Presets() + presets.load() + + print("Starting roll pattern test...") + + ref_len = presets.strip_length(0) + # Use some margins based on strip length to show different bands. + quarter = ref_len // 4 + + # Define a few different roll presets: + roll_presets = [] + + # 1. Full-strip, white -> off, clockwise, medium speed + roll_presets.append( + make_roll_preset( + "roll_full_cw", + color1=(255, 255, 255), + color2=(0, 0, 0), + n1=0, + n2=0, + n3=2, # 2 rotations then stop + n4=0, # clockwise + delay_ms=40, + brightness=255, + ) + ) + + # 2. Inner band only, red -> blue, clockwise, slower + roll_presets.append( + make_roll_preset( + "roll_inner_band", + color1=(255, 0, 0), + color2=(0, 0, 255), + n1=quarter, # start margin + n2=quarter, # end margin + n3=2, + n4=0, + delay_ms=60, + brightness=255, + ) + ) + + # 3. Full-strip, green -> off, counter-clockwise, faster + roll_presets.append( + make_roll_preset( + "roll_full_ccw", + color1=(0, 255, 0), + color2=(0, 0, 0), + n1=0, + n2=0, + n3=2, + n4=1, # counter-clockwise + delay_ms=30, + brightness=255, + ) + ) + + # Run each roll preset for about 5 seconds + for name, preset_obj in roll_presets: + print("Running roll preset:", name) + run_preset(presets, name, preset_obj, duration_ms=5000) + + print("Roll pattern test finished. Turning off LEDs.") + presets.select("off") + presets.tick() + + +if __name__ == "__main__": + main() + diff --git a/pico/test/test_scale.py b/pico/test/test_scale.py new file mode 100644 index 0000000..a1370a4 --- /dev/null +++ b/pico/test/test_scale.py @@ -0,0 +1,100 @@ +""" +Test the Presets.scale() helper on-device with mpremote. + +Usage (from project root): + + mpremote connect cp pico/src/*.py : && + mpremote connect cp pico/src/patterns/*.py :patterns && + mpremote connect cp pico/lib/*.py : && + mpremote connect cp tests/test_scale.py : && + mpremote connect run test_scale.py + +This script: + - Creates a minimal Presets instance + - Runs a few numeric test cases for scale() + - Optionally displays a short visual check on the LEDs +""" + +from presets import Presets + + +def numeric_tests(presets): + """ + Numeric sanity checks for scale() using the actual strip config. + + We treat strip 0 as the reference and print the mapped indices for + a few positions on each other strip. + """ + print("Numeric scale() tests (from strip 0):") + ref_len = presets.strip_length(0) + if ref_len <= 0: + print(" strip 0 length <= 0; skipping numeric tests.") + return + + test_positions = [0, ref_len // 2, ref_len - 1] + for pos in test_positions: + print(" pos on strip 0:", pos) + for dst_idx in range(len(presets.strips)): + dst_len = presets.strip_length(dst_idx) + if dst_len <= 0: + continue + n2 = presets.scale(dst_idx, pos) + print(" -> strip", dst_idx, "len", dst_len, "pos", n2) + + +def visual_test(presets): + """ + Simple visual test: + - Use strip 0 as reference + - Move a pixel along strip 0 + - Map position to all other strips with scale() + """ + import utime + + strips = presets.strips + if not strips: + print("No strips available for visual test.") + return + + src_strip_idx = 0 + l1 = presets.strip_length(src_strip_idx) + if l1 <= 0: + print("strip_length(0) <= 0; aborting visual test.") + return + + color = (50, 0, 0) # dim red so it doesn't blind you + + # Run once across the full length of the reference strip, + # jumping 10 LEDs at a time. + step_size = 10 + steps = (l1 + step_size - 1) // step_size + print("Starting visual scale() test with 10-LED jumps:", steps, "steps...") + for step in range(steps): + n1 = (step * step_size) % l1 + + # Clear all strips + for strip in strips: + strip.fill((0, 0, 0)) + + # Light mapped position on each strip using Presets.set/show + for dst_strip_idx, _ in enumerate(strips): + presets.set(dst_strip_idx, n1, color) + presets.show(dst_strip_idx) + + print("Visual test finished.") + + +def main(): + presets = Presets() + presets.load() + numeric_tests(presets) + # Comment this in/out depending on whether you want the LEDs to run: + try: + visual_test(presets) + except Exception as e: + print("Visual test error:", e) + + +if __name__ == "__main__": + main() +