import math import time from machine import Pin import neopixel class BoardLED: """Utility class to control the onboard WS2812 (NeoPixel) RGB LED on GPIO 38.""" def __init__(self, pin_num=38): self.np = neopixel.NeoPixel(Pin(pin_num), 1) self.base_color = (0, 0, 0) self.off() def set_color(self, r, g, b): """Sets the RGB LED to a specific static color. Values are between 0 and 255. """ self.base_color = (r, g, b) self.np[0] = (r, g, b) self.np.write() def off(self): """Turns the RGB LED off.""" self.set_color(0, 0, 0) def update_breathing(self, speed_factor=1.0): """Performs one step of a non-blocking breathing animation. Call this regularly in the main application loop. Args: speed_factor (float): Speeds up or slows down the breathing rate. """ if self.base_color == (0, 0, 0): return t = time.ticks_ms() / 1000.0 * speed_factor # Sine wave from 0.05 to 1.0 for breathing intensity factor = 0.525 + 0.475 * math.sin(t * math.pi) br = int(self.base_color[0] * factor) bg = int(self.base_color[1] * factor) bb = int(self.base_color[2] * factor) self.np[0] = (br, bg, bb) self.np.write() def update_rainbow(self, speed_factor=0.2): """Performs one step of a non-blocking rainbow color cycle. Call this regularly in the main application loop. """ t = time.ticks_ms() / 1000.0 * speed_factor # Use phase offsets of 120 degrees (2pi/3) for red, green, blue r = int(127.5 * (1.0 + math.sin(t * 2.0 * math.pi))) g = int(127.5 * (1.0 + math.sin(t * 2.0 * math.pi + 2.0 * math.pi / 3.0))) b = int(127.5 * (1.0 + math.sin(t * 2.0 * math.pi + 4.0 * math.pi / 3.0))) self.np[0] = (r, g, b) self.np.write()