Warning:

To protect the digital assets of creators and enhance system security and stability, the CyberBrick Multi-Function Core Board does not support firmware flashing with any third-party tools! If you need to restore the Multi-Function Core Board to its factory state, please wait for the official recovery tool to be released by CyberBrick. If you proceed to flash the firmware using third-party tools, the firmware of the Multi-Function Core Board will be permanently damaged and cannot be recovered. CyberBrick will not be responsible for any consequences resulting from such operations.

This documentation is adapted from the official MicroPython project. The CyberBrick team has extended the source code with custom interfaces and features to our hardware and application needs.

In addition to these enhancements, certain built-in MicroPython interfaces have been intentionally disabled to protect the system's integrity, and ensure the overall security and reliability of the device. This also facilitates content protection for creators' intellectual property, where applicable.

Portions of the content are derived from the official MicroPython documentation and have been included here under its open-source license to provide users with a consistent and enriched development experience tailored to the CyberBrick platform.

Source code for bbl.leds

# -*-coding:utf-8-*-
#
# The CyberBrick Codebase License, see the file LICENSE for details.
#
# Copyright (c) 2025 MakerWorld
#

from machine import Pin
from machine import bitstream
import utime
import math

LED_CHANNEL1 = 21
LED_CHANNEL2 = 20


[docs] class NeoPixel: # NeoPixel driver for MicroPython # MIT license; Copyright (c) 2016 Damien P. George, 2021 Jim Mussared # G R B W ORDER = (1, 0, 2, 3)
[docs] def __init__(self, pin, n, bpp=3, timing=1): self.pin = pin self.n = n self.bpp = bpp self.buf = bytearray(n * bpp) self.pin.init(pin.OUT) # or a user-specified timing ns tuple (high_0, low_0, high_1, low_1). self.timing = ( ((400, 850, 800, 450) if timing else (400, 1000, 1000, 400)) if isinstance(timing, int) else timing )
def __len__(self): return self.n def __setitem__(self, i, v): offset = i * self.bpp for i in range(self.bpp): self.buf[offset + self.ORDER[i]] = v[i] def __getitem__(self, i): offset = i * self.bpp return tuple(self.buf[offset + self.ORDER[i]] for i in range(self.bpp))
[docs] def fill(self, v): b = self.buf l = len(self.buf) bpp = self.bpp for i in range(bpp): c = v[i] j = self.ORDER[i] while j < l: b[j] = c j += bpp
[docs] def write(self): # BITSTREAM_TYPE_HIGH_LOW = 0 bitstream(self.pin, 0, self.timing, self.buf)
[docs] class LEDController: """ A singleton class to control an LED. """ _instances = {} def __new__(cls, led_channel, *args, **kwargs): if led_channel not in cls._instances: cls._instances[led_channel] = super(LEDController, cls).__new__(cls) return cls._instances[led_channel]
[docs] def __init__(self, led_channel): """ Initializes the LEDController instance for controlling an LED based \ on the specified channel. This method sets up the LED effects, initializes the current effect \ index, repeat count, duration, and start time. It then maps the provided LED channel to its corresponding pin number \ and initializes the NeoPixel object. Args: led_channel (str): The channel number of the LED, either "LED1" \ or "LED2". Raises: ValueError: If the provided led_channel is not "LED1" or "LED2". Example: >>> led_controller = LEDController("LED1") >>> # The LEDController instance is now initialized with LED1's \ pin configuration. Note: The led_channel parameter should be a string matching either \ "LED1" or "LED2". The actual pin numbers for "LED1" and "LED2" are defined in the \ led_pins_map dictionary. The NeoPixel object is initialized with the pin number and the \ number of LEDs (4 in this case). See Also: NeoPixel: The class used to control the NeoPixel LED strip. """ # Ensure __init__ only initializes once if hasattr(self, '_initialized') and self._initialized: return self._initialized = True # Map LED1 and LED2 to their respective pin numbers self.led_pins_map = {"LED1": LED_CHANNEL1, "LED2": LED_CHANNEL2} if led_channel not in self.led_pins_map: raise ValueError("Invalid LED channel") self.effects = [ self._solid_effect, self._blink_effect, self._breathing_effect ] self.channel = led_channel self.current_effect_index = 0 self.repeat_count = 0 self.duration = 0 self.current_effect_start_time = 0 self.duty_cycle = 0 self.led_index = 0 self.rgb = 0x000000 self.is_on = False pin = Pin(self.led_pins_map[led_channel], Pin.OUT) self.np = NeoPixel(pin, 4, timing=0) for i in range(4): self.np[i] = (0, 0, 0) self.np.write()
[docs] def reinit(self): self.current_effect_index = 0 self.repeat_count = 0 self.duration = 0 self.current_effect_start_time = 0 pin = Pin(self.led_pins_map[self.channel], Pin.OUT) self.np = NeoPixel(pin, 4, timing=0)
def _breathing_effect(self): current_time = utime.ticks_ms() elapsed_time = utime.ticks_diff(current_time, self.current_effect_start_time) # Calculate duty cycle based on elapsed time and duration progress = (elapsed_time % self.duration) / self.duration # Sine wave pattern for smooth breathing (0 to 1 to 0) self.duty_cycle = int(512 * (1 + math.sin(2 * math.pi * progress - math.pi/2))) # Calculate the duty cycle factor once for use in all colors duty_factor = self.duty_cycle / 1024.0 for i in range(4): if self.led_index & (1 << i): red = int(((self.rgb >> 16) & 0xFF) * duty_factor) green = int(((self.rgb >> 8) & 0xFF) * duty_factor) blue = int((self.rgb & 0xFF) * duty_factor) self.np[i] = (red, green, blue) else: self.np[i] = (0, 0, 0) self.np.write() def _blink_effect(self): current_time = utime.ticks_ms() elapsed_time = utime.ticks_diff(current_time, self.current_effect_start_time) if elapsed_time < self.duration / 2: if self.is_on is False: self.is_on = True for i in range(4): if self.led_index & (1 << i): self.np[i] = ((self.rgb >> 16) & 0xFF, (self.rgb >> 8) & 0xFF, self.rgb & 0xFF) else: self.np[i] = (0, 0, 0) self.np.write() else: if self.is_on is True: self.is_on = False for i in range(4): self.np[i] = (0, 0, 0) self.np.write() def _solid_effect(self): if self.is_on is False: self.is_on = True for i in range(4): if self.led_index & (1 << i): self.np[i] = ((self.rgb >> 16) & 0xFF, (self.rgb >> 8) & 0xFF, self.rgb & 0xFF) else: self.np[i] = (0, 0, 0) pass self.np.write()
[docs] def timing_proc(self): """ Callback function to update the LED effect. This method is called at regular intervals to update the current \ LED effect. Args: None Returns: None """ current_effect = self.effects[self.current_effect_index] current_effect() self._update_effect()
def _update_effect(self): current_time = utime.ticks_ms() elapsed_time = utime.ticks_diff(current_time, self.current_effect_start_time) if elapsed_time >= self.duration: if self.repeat_count != 0xFF: self.repeat_count -= 1 if self.repeat_count > 0: self.current_effect_start_time = current_time
[docs] def set_led_effect(self, mod, duration, repeat_count, led_index, rgb): """ Sets the LED effect. This method configures the LED with the specified effect, duration, repeat count, LED index, and RGB color. Args: mod (int): The index of the effect to set. - mod = 0: solid effect - mod = 1: blink effect - mod = 2: breathing effect duration (int): The duration of the effect in milliseconds. repeat_count (int): The number of times to repeat the effect. Must be between 0 and 255. A value of 255( 0xFF ) represents infinite repetition. led_index (int): The index of the LED to control. Each bit represents the index of an LED (e.g., the first bit represents OUT1, the second bit represents OUT2). rgb (int): The RGB color value of the LED in hexadecimal. Returns: None Raises: ValueError: If mod, repeat_count, or led_index is out of range, or if rgb is not a valid hexadecimal color code. Example: >>> # Solid red on LED1 for 1 second >>> set_led_effect(0, 1000, 5, 0b0001, 0xFF0000) >>> # Blink green on LED1 and LED2 indefinitely >>> set_led_effect(1, 500, 255, 0b0011, 0x00FF00) """ if not 0 <= mod < len(self.effects): print("[LEDS]Invalid effect index. Must be between 0 and 2.") return if not isinstance(repeat_count, int) or repeat_count < 0 or repeat_count > 255: print("[LEDS]Invalid repeat count.") return self.current_effect_index = mod self.duration = duration self.repeat_count = repeat_count self.duty_cycle = 0 self.led_index = led_index self.rgb = rgb self.is_on = False self.current_effect_start_time = utime.ticks_ms()
if __name__ == '__main__': import uasyncio async def _main(): COLOR_WHITE = 0xFFFFFF COLOR_LIGHT_BLUE = 0x40CFFF COLOR_LIGHT_RED = 0xFF4040 led_1 = LEDController("LED1") led_2 = LEDController("LED2") async def period_task(): while True: led_1.timing_proc() led_2.timing_proc() await uasyncio.sleep(0.01) async def ctrl_task(): while True: led_1.set_led_effect(2, 800, 0xFF, 0x0F, COLOR_WHITE) led_2.set_led_effect(2, 800, 0xFF, 0x0F, COLOR_WHITE) await uasyncio.sleep(1.6) led_1.set_led_effect(1, 800, 0xFF, 0x0F, COLOR_LIGHT_BLUE) led_2.set_led_effect(1, 800, 0xFF, 0x0F, COLOR_LIGHT_BLUE) await uasyncio.sleep(1.6) led_1.set_led_effect(0, 0, 0xFF, 0x0F, COLOR_LIGHT_RED) led_2.set_led_effect(0, 0, 0xFF, 0x0F, COLOR_LIGHT_RED) await uasyncio.sleep(1.6) await uasyncio.gather(period_task(), ctrl_task()) uasyncio.run(_main())