Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cosmic #641

Merged
merged 13 commits into from
Feb 21, 2023
Merged

Cosmic #641

merged 13 commits into from
Feb 21, 2023

Conversation

ageeandakeyboard
Copy link
Contributor

drivers and MP implementation for Cosmic unicorn

@ZodiusInfuser ZodiusInfuser added documentation Improvements or additions to documentation [- pico graphics library -] micropython This issue or request relates to micropython (either code or bindings) c++ This issue or request relates to C++ code labels Jan 20, 2023
if(unicorn == this) {
if(graphics->pen_type == PicoGraphics::PEN_RGB888) {
uint32_t *p = (uint32_t *)graphics->frame_buffer;
for(size_t j = 0; j < 32 * 32; j++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just loop through y and x values here, since we already know their ranges and calculating them from j is unnecessarily costly.

@Gadgetoid Gadgetoid force-pushed the cosmic branch 12 times, most recently from 765a086 to bac4a0b Compare February 7, 2023 19:53
@Gadgetoid
Copy link
Member

Using ulab/numpy on the latest build (gosh I hope we don't blow the firmware size here) this code will get ~60FPS fire:

import time
import random
from cosmic import CosmicUnicorn
from picographics import PicoGraphics, DISPLAY_COSMIC_UNICORN, PEN_P8
from ulab import numpy
import machine

# MAXIMUM OVERKILL
machine.freq(250_000_000)

gu = CosmicUnicorn()
graphics = PicoGraphics(DISPLAY_COSMIC_UNICORN, pen_type=PEN_P8)

PALETTE_SIZE = 8

# Create the fire palette
graphics.create_pen(0, 0, 0)
graphics.create_pen(0, 0, 0)
graphics.create_pen(20, 20, 20)
graphics.create_pen(100, 30, 0)
graphics.create_pen(180, 30, 0)
graphics.create_pen(220, 160, 0)
graphics.create_pen(255, 255, 180)
graphics.create_pen(255, 255, 220)


@micropython.native  # noqa: F821
def update():
    # take local references as it's quicker than accessing the global
    # and we access it a lot in this method
    _heat = heat
    _randint = random.randint
    _height = height
    
    # clear the bottom row and then add a new fire seed to it
    _heat[height - 1][:] = 0.0
    _heat[height - 2][:] = 0.0

    for c in range(fire_spawns):
        x = _randint(0, width - 4) + 2
        _heat[_height - 1][x - 1:x + 1] = 1.0
        _heat[_height - 2][x - 1:x + 1] = 1.0

    # y + 1, x
    a = numpy.roll(_heat, -1, axis=0)
    # y + 2, x
    b = numpy.roll(_heat, -2, axis=0)
    
    # y + 1
    c = numpy.roll(_heat, -1, axis=0)
    # y + 1, x - 1
    d = numpy.roll(c,  1, axis=1)
    e = numpy.roll(c, -1, axis=1) 

    # Average over 5 adjacent pixels and apply damping
    _heat += a + b + d + e
    _heat[:] *= damping_factor / 5.0


@micropython.native  # noqa: F821
def draw():
    memoryview(graphics)[:] = numpy.ndarray(heat[0:32, 0:32] * PALETTE_SIZE, dtype=numpy.uint8).tobytes()
    gu.update(graphics)


width = CosmicUnicorn.WIDTH
height = CosmicUnicorn.HEIGHT + 4
heat = numpy.zeros((height, width))
fire_spawns = 5
damping_factor = 0.999

gu.set_brightness(0.5)

while True:
    if gu.is_pressed(CosmicUnicorn.SWITCH_BRIGHTNESS_UP):
        gu.adjust_brightness(+0.01)

    if gu.is_pressed(CosmicUnicorn.SWITCH_BRIGHTNESS_DOWN):
        gu.adjust_brightness(-0.01)

    tstart = time.ticks_ms()
    update()
    tupdate = time.ticks_ms()
    draw()
    tfinish = time.ticks_ms()
    
    total = tfinish - tstart

    #print(f"Update: {tupdate-tstart}, Draw: {tfinish-tupdate}, Total: {tfinish-tstart}")

    # pause for a moment (important or the USB serial device will fail)
    # try to pace at 60fps or 30fps
    if total > 1000 / 30:
        time.sleep(0.0001)  
    elif total > 1000 / 60:
        t = 1000 / 30 - total
        time.sleep(t / 1000)
    else:
        t = 1000 / 60 - total
        time.sleep(t / 1000)

Palette could probably use a little finesse.

@Gadgetoid
Copy link
Member

Okay scratch that, now we have an extensible palette and everything's driven by numpy you can PUMP UP THE FIREEEEEEEE:

import time
import random
from cosmic import CosmicUnicorn
from picographics import PicoGraphics, DISPLAY_COSMIC_UNICORN, PEN_P8
from ulab import numpy
import machine

# MAXIMUM OVERKILL
machine.freq(250_000_000)

gu = CosmicUnicorn()
graphics = PicoGraphics(DISPLAY_COSMIC_UNICORN, pen_type=PEN_P8)

# Number of random fire spawns
FIRE_SPAWNS = 5

# Fire damping
DAMPING_FACTOR = 0.98

# TURN UP THE HEEEAAT
HEAT = 3.0

# Create the fire palette
graphics.create_pen(0, 0, 0)
graphics.create_pen(0, 0, 0)
graphics.create_pen(20, 20, 20)
graphics.create_pen(50, 10, 0)
graphics.create_pen(180, 30, 0)
graphics.create_pen(220, 160, 0)
graphics.create_pen(255, 255, 180)
graphics.create_pen(255, 255, 220)
graphics.create_pen(0, 0, 255)

PALETTE_SIZE = 9 # Should match the number of colours defined above

# Bit of a buffer so SUPER HOT fire doesn't turn black
for x in range(10):
    graphics.create_pen(255, 255, 255)


@micropython.native  # noqa: F821
def update():
    # take local references as it's quicker than accessing the global
    # and we access it a lot in this method
    _heat = heat
    _randint = random.randint
    _height = height
    
    # clear the bottom row and then add a new fire seed to it
    _heat[height - 1][:] = 0.0
    _heat[height - 2][:] = 0.0

    for c in range(FIRE_SPAWNS):
        x = _randint(0, width - 4) + 2
        _heat[_height - 1][x - 1:x + 1] = HEAT / 2.0
        _heat[_height - 2][x - 1:x + 1] = HEAT

    # y + 1, x
    a = numpy.roll(_heat, -1, axis=0)
    # y + 2, x
    b = numpy.roll(_heat, -2, axis=0)
    
    # y + 1
    c = numpy.roll(_heat, -1, axis=0)
    # y + 1, x - 1
    d = numpy.roll(c,  1, axis=1)
    e = numpy.roll(c, -1, axis=1) 

    # Average over 5 adjacent pixels and apply damping
    _heat += a + b + d + e
    _heat[:] *= DAMPING_FACTOR / 5.0


@micropython.native  # noqa: F821
def draw():
    memoryview(graphics)[:] = numpy.ndarray(heat[0:32, 0:32] * PALETTE_SIZE, dtype=numpy.uint8).tobytes()
    gu.update(graphics)


width = CosmicUnicorn.WIDTH
height = CosmicUnicorn.HEIGHT + 4
heat = numpy.zeros((height, width))

gu.set_brightness(0.5)

while True:
    if gu.is_pressed(CosmicUnicorn.SWITCH_BRIGHTNESS_UP):
        gu.adjust_brightness(+0.01)

    if gu.is_pressed(CosmicUnicorn.SWITCH_BRIGHTNESS_DOWN):
        gu.adjust_brightness(-0.01)

    tstart = time.ticks_ms()
    update()
    tupdate = time.ticks_ms()
    draw()
    tfinish = time.ticks_ms()
    
    total = tfinish - tstart

    #print(f"Update: {tupdate-tstart}, Draw: {tfinish-tupdate}, Total: {tfinish-tstart}")

    # pause for a moment (important or the USB serial device will fail)
    # try to pace at 60fps or 30fps
    if total > 1000 / 30:
        time.sleep(0.0001)  
    elif total > 1000 / 60:
        t = 1000 / 30 - total
        time.sleep(t / 1000)
    else:
        t = 1000 / 60 - total
        time.sleep(t / 1000)

@Gadgetoid
Copy link
Member

Gadgetoid commented Feb 13, 2023

TODO

Documentation

Same as Galactic?

Examples

Needs some examples that demonstrate networking

  • - Sunrise / Sunset API
  • - Cheerlights (cheerlights_history.py)
  • - Clock
  • - Based web page example (access point and WiFi?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[- pico graphics library -] c++ This issue or request relates to C++ code documentation Improvements or additions to documentation micropython This issue or request relates to micropython (either code or bindings)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants