# neopixel_demo.py

# Raspberry Pi Pico - NeoPixel LED array demo

# Write an animated pattern to a strand of NeoPixel LEDS, using only the
# low-level neopixel_write module.

################################################################
# CircuitPython module documentation:
# time       https://circuitpython.readthedocs.io/en/latest/shared-bindings/time/index.html
# board      https://circuitpython.readthedocs.io/en/latest/shared-bindings/board/index.html
# digitalio  https://circuitpython.readthedocs.io/en/latest/shared-bindings/digitalio/index.html
# neopixel_write  https://circuitpython.readthedocs.io/en/latest/shared-bindings/neopixel_write/index.html
#

################################################################################
# load standard Python modules
import time

# load the CircuitPython hardware definition module for pin definitions
import board

# load the CircuitPython GPIO support
import digitalio

# load the CircuitPython NeoPixel output support
import neopixel_write

#---------------------------------------------------------------
# The NeoPixel strand uses one GPIO output.  Note that in general it will need
# separate 5V power, only the smallest arrays can use USB power.
pixels = digitalio.DigitalInOut(board.GP13)
pixels.direction = digitalio.Direction.OUTPUT

# The number of NeoPixels must be specified, as they cannot be detected.  This
# demo can support hundreds of NeoPixels: at 200 pixels, the frame rate is still
# 30 Hz, at 400 pixels that drops to 17 Hz.  This could be improved: the data
# rate is 800 kHz, so with a 30Hz frame rate the strand can theoretically be
# 1100 pixels.
num_pixels = 12

# Create a buffer for storing a complete linear image.  The color order and
# depth depends upon the specific devices, but are typically 3 bytes (24 bits)
# per pixel in BGR order.  The first pixel data will apply to the first device
# in the chain, the second to the next, etc.  Excess data will have no effect.
# If less than a full frame is sent, the last pixels will not change.
frame_buffer = bytearray(3*num_pixels)

#---------------------------------------------------------------
# Run the main loop to generate a sequence of frames as fast as possible.

while True:

    # generate the next frame based on the real-time clock value
    now = time.monotonic_ns() / 1000
    
    # generate a temporal color sequence with each component out of phase
    red = int((now//11000) % 256)
    grn = int((now//33000) % 256)
    blu = int((now//55000) % 256)

    # print(f"{red}, {grn}, {blu}")

    # update the entire frame buffer including an additional position-dependent term
    # to create spatial variation
    for p in range(num_pixels):
        frame_buffer[3*p]   = (grn + 12*p) % 256
        frame_buffer[3*p+1] = (red + 12*p) % 256
        frame_buffer[3*p+2] = (blu + 12*p) % 256

    # transfer the new frame to the NeoPixel LED strand
    neopixel_write.neopixel_write(pixels, frame_buffer)
