Files
Ninluc 7299a50198
Build, push image, and notify Watchtower / build-image (push) Successful in 1m21s
Build, push image, and notify Watchtower / notify (push) Successful in 17s
Beginning of cooking cycle
2026-07-31 21:50:42 +02:00

159 lines
5.1 KiB
Python

from machine import Pin, PWM, Timer
import time
class RGBLED:
"""
MicroPython driver for 4-pin RGB LEDs on ESP32 / Heltec boards.
Supports state tracking, color setting, brightness scaling,
state toggling, and non-blocking blinking via machine.Timer.
"""
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 120, 0)
WHITE_YELLOW = (150, 30, 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, timer_id=1):
"""
: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)
:param timer_id: Hardware/software timer ID for non-blocking blinks (-1 uses soft timers on ESP32).
"""
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
# Blink state variables
self._timer = Timer(timer_id)
self._is_blinking = False
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
@property
def is_blinking(self):
return self._is_blinking
# --- Basic Control 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()
# --- Non-Blocking Blinking Methods ---
def _timer_callback(self, t):
"""Internal callback executed by machine.Timer."""
self.toggle()
def blink_on(self, interval_ms=500):
"""Starts background blinking at the specified interval in milliseconds."""
if self._is_blinking:
self._timer.deinit()
self._is_blinking = True
self.on() # Ensure initial state is on
self._timer.init(
period=interval_ms,
mode=Timer.PERIODIC,
callback=self._timer_callback
)
def blink_off(self):
"""Stops blinking and returns control to steady state."""
if self._is_blinking:
self._timer.deinit()
self._is_blinking = False
def blink_toggle(self, interval_ms=500):
"""Toggles blinking state (starts if stopped, stops if active)."""
if self._is_blinking:
self.blink_off()
else:
self.blink_on(interval_ms)
def deinit(self):
"""Releases the hardware PWM pins and timer when finished."""
self._r_pwm.deinit()
self._g_pwm.deinit()
self._b_pwm.deinit()