import utime class ColourCycle: def __init__(self, driver): self.driver = driver def _render(self, colors, phase, brightness): num_leds = self.driver.num_leds color_count = len(colors) if num_leds <= 0 or color_count <= 0: return if color_count == 1: self.driver.fill(self.driver.apply_brightness(colors[0], brightness)) return full_span = color_count * 256 # Match rainbow behaviour: phase is 0..255 and maps to one full-strip shift. phase_shift = (phase * full_span) // 256 for i in range(num_leds): # Position around the colour loop, shifted by phase. pos = ((i * full_span) // num_leds + phase_shift) % full_span idx = pos // 256 frac = pos & 255 c1 = colors[idx] c2 = colors[(idx + 1) % color_count] blended = ( c1[0] + ((c2[0] - c1[0]) * frac) // 256, c1[1] + ((c2[1] - c1[1]) * frac) // 256, c1[2] + ((c2[2] - c1[2]) * frac) // 256, ) self.driver.n[i] = self.driver.apply_brightness(blended, brightness) self.driver.n.write() def run(self, preset): colors = preset.c if preset.c else [(255, 255, 255)] phase = self.driver.step % 256 step_amount = max(1, int(preset.n1)) if not preset.a: self._render(colors, phase, preset.b) self.driver.step = (phase + step_amount) % 256 yield return last_update = utime.ticks_ms() while True: current_time = utime.ticks_ms() delay_ms = max(1, int(preset.d)) if utime.ticks_diff(current_time, last_update) >= delay_ms: self._render(colors, phase, preset.b) phase = (phase + step_amount) % 256 self.driver.step = phase last_update = utime.ticks_add(last_update, delay_ms) yield