from machine import Pin, PWM class RGBLED: """ MicroPython driver for 4-pin RGB LEDs on ESP32 / Heltec boards. Supports state tracking, color setting, brightness scaling, and state toggling. """ # Preset RGB tuples for quick use RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) YELLOW = (255, 150, 0) ORANGE = (255, 50, 0) WHITE = (255, 255, 255) OFF = (0, 0, 0) def __init__(self, red_pin, green_pin, blue_pin, common_anode=False, freq=1000): """ :param red_pin: GPIO pin number for Red channel :param green_pin: GPIO pin number for Green channel :param blue_pin: GPIO pin number for Blue channel :param common_anode: Set True if cathode is connected to 3.3V instead of GND :param freq: PWM frequency in Hz (default 1000Hz) """ self._r_pwm = PWM(Pin(red_pin, Pin.OUT), freq=freq) self._g_pwm = PWM(Pin(green_pin, Pin.OUT), freq=freq) self._b_pwm = PWM(Pin(blue_pin, Pin.OUT), freq=freq) self._common_anode = common_anode # State tracking variables self._color = (0, 0, 0) # Current (R, G, B) tuple [0-255] self._brightness = 1.0 # Brightness factor [0.0 to 1.0] self._is_on = True # Master power state self._apply() def _apply(self): """Recalculates and applies PWM duty cycles based on state.""" if not self._is_on: r, g, b = 0, 0, 0 else: r = int(self._color[0] * self._brightness) g = int(self._color[1] * self._brightness) b = int(self._color[2] * self._brightness) for pwm, val in ((self._r_pwm, r), (self._g_pwm, g), (self._b_pwm, b)): # Clamp value between 0 and 255 val = max(0, min(255, val)) # Convert 8-bit (0-255) to MicroPython's 16-bit PWM duty (0-65535) duty = int((val / 255.0) * 65535) if self._common_anode: duty = 65535 - duty pwm.duty_u16(duty) # --- Properties and Setters --- @property def color(self): """Returns the active RGB tuple (R, G, B).""" return self._color @color.setter def color(self, rgb_tuple): """Sets the RGB color tuple (e.g., (255, 128, 0)).""" if isinstance(rgb_tuple, (tuple, list)) and len(rgb_tuple) == 3: self._color = tuple(rgb_tuple) self._apply() else: raise ValueError("Color must be a tuple of 3 integers: (R, G, B)") @property def brightness(self): """Returns the current brightness level (0.0 to 1.0).""" return self._brightness @brightness.setter def brightness(self, level): """Sets brightness level from 0.0 (0%) to 1.0 (100%).""" self._brightness = max(0.0, min(1.0, float(level))) self._apply() @property def is_on(self): """Returns True if the LED is currently powered on.""" return self._is_on # --- Helper Methods --- def set_rgb(self, r, g, b): """Alternative setter for individual R, G, B integer values.""" self.color = (r, g, b) def on(self): """Turns the LED on using its stored color and brightness.""" self._is_on = True self._apply() def off(self): """Turns the LED off without resetting the active color state.""" self._is_on = False self._apply() def toggle(self): """Toggles between ON and OFF states.""" self._is_on = not self._is_on self._apply() def deinit(self): """Releases the hardware PWM pins when finished.""" self._r_pwm.deinit() self._g_pwm.deinit() self._b_pwm.deinit()