Copied Cosmic MPy examples to Stellar
|
@ -152,8 +152,6 @@ tone_b = 0
|
|||
# The current synth beat
|
||||
beat = 0
|
||||
|
||||
text = ""
|
||||
|
||||
|
||||
def next_beat():
|
||||
global beat
|
||||
|
@ -312,6 +310,8 @@ while True:
|
|||
# print("white gradient")
|
||||
gradient(255, 255, 255)
|
||||
|
||||
text = ""
|
||||
|
||||
if cu.is_pressed(CosmicUnicorn.SWITCH_A):
|
||||
text = "PlaySyn"
|
||||
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
# Stellar Unicorn MicroPython Examples <!-- omit in toc -->
|
||||
|
||||
- [About Stellar Unicorn](#about-stellar-unicorn)
|
||||
- [Stellar Unicorn and PicoGraphics](#stellar-unicorn-and-picographics)
|
||||
- [Examples](#examples)
|
||||
- [Clock](#clock)
|
||||
- [Eighties Super Computer](#eighties-super-computer)
|
||||
- [Feature Test](#feature-test)
|
||||
- [Feature Test With Audio](#feature-test-with-audio)
|
||||
- [Fire Effect](#fire-effect)
|
||||
- [Lava Lamp](#lava-lamp)
|
||||
- [Nostalgia Prompt](#nostalgia-prompt)
|
||||
- [Rainbow](#rainbow)
|
||||
- [Scrolling Text](#scrolling-text)
|
||||
- [Today](#today)
|
||||
- [Wireless Examples](#wireless-examples)
|
||||
- [Cheerlights History](#cheerlights-history)
|
||||
- [Stellar Paint](#stellar-paint)
|
||||
- [Exchange Ticker](#exchange-ticker)
|
||||
- [HTTP Text](#http-text)
|
||||
- [Weather](#weather)
|
||||
- [NumPy examples](#numpy-examples)
|
||||
- [Other Examples](#other-examples)
|
||||
- [Launch (Demo Reel)](#launch-demo-reel)
|
||||
- [Other Resources](#other-resources)
|
||||
|
||||
## About Stellar Unicorn
|
||||
|
||||
Stellar Unicorn offers 16x16 bright RGB LEDs driven by Pico W's PIO in addition to a 1W amplifier + speaker, a collection of system and user buttons, and two Qw/ST connectors for adding external sensors and devices. Woha!
|
||||
|
||||
- :link: [Stellar Unicorn store page](https://shop.pimoroni.com/products/stellar-unicorn)
|
||||
|
||||
Stellar Unicorn ships with MicroPython firmware pre-loaded, but you can download the most recent version at the link below (you'll want the `stellar-unicorn` image).
|
||||
|
||||
- [MicroPython releases](https://github.com/pimoroni/pimoroni-pico/releases)
|
||||
- [Installing MicroPython](../../../setting-up-micropython.md)
|
||||
|
||||
## Stellar Unicorn and PicoGraphics
|
||||
|
||||
The easiest way to start displaying cool stuff on Stellar Unicorn is using our Stellar Unicorn module (which contains a bunch of helpful functions for interacting with the buttons, adjusting brightness and suchlike) and our PicoGraphics library, which is chock full of useful functions for drawing on the LED matrix.
|
||||
|
||||
- [Stellar Unicorn function reference](../../modules/stellar_unicorn/README.md)
|
||||
- [PicoGraphics function reference](../../modules/picographics/README.md)
|
||||
|
||||
## Examples
|
||||
|
||||
### Clock
|
||||
|
||||
[clock.py](clock.py)
|
||||
|
||||
Clock example with (optional) NTP synchronization. You can adjust the brightness with LUX + and -, and resync the time by pressing A.
|
||||
|
||||
### Eighties Super Computer
|
||||
|
||||
[eighties_super_computer.py](eighties_super_computer.py)
|
||||
|
||||
Random LEDs blink on and off mimicing the look of a movie super computer doing its work in the eighties. You can adjust the brightness with LUX + and -.
|
||||
|
||||
### Feature Test
|
||||
|
||||
[feature_test.py](feature_test.py)
|
||||
|
||||
Displays some text, gradients and colours and demonstrates button use. You can adjust the brightness with LUX + and -.
|
||||
|
||||
### Feature Test With Audio
|
||||
|
||||
[feature_test_with_audio.py](feature_test_with_audio.py)
|
||||
|
||||
Displays some text, gradients and colours and demonstrates button use. Also demonstrates some of the audio / synth features.
|
||||
- Button A plays a synth tune
|
||||
- Button B plays a solo channel of the synth tune
|
||||
- Button C plays a sinewave (it's frequency can be adjusted with VOL + and -)
|
||||
- Button D plays a second sinewave (it's frequency can be adjusted with LUX + and -)
|
||||
- Sleep button stops the sounds
|
||||
|
||||
### Fire Effect
|
||||
|
||||
[fire_effect.py](fire_effect.py)
|
||||
|
||||
A pretty, procedural fire effect. Switch between landscape fire and vertical fire using the A and B buttons! You can adjust the brightness with LUX + and -.
|
||||
|
||||
### Lava Lamp
|
||||
|
||||
[lava_lamp.py](lava_lamp.py)
|
||||
|
||||
A 70s-tastic, procedural rainbow lava lamp. You can adjust the brightness with LUX + and -.
|
||||
|
||||
### Nostalgia Prompt
|
||||
|
||||
[nostalgia_prompt.py](nostalgia_prompt.py)
|
||||
|
||||
A collection of copies of classic terminal styles including C64, MS-DOS, Spectrum, and more. Images and text are drawn pixel by pixel from a pattern of Os and Xs. You can adjust the brightness with LUX + and -.
|
||||
|
||||
### Rainbow
|
||||
|
||||
[rainbow.py](rainbow.py)
|
||||
|
||||
Some good old fashioned rainbows! You can adjust the cycling speed with A and B, stripe width with C and D, hue with VOL + and -, and the brightness with LUX + and -. The sleep button stops the animation (can be started again with A or B).
|
||||
|
||||
### Scrolling Text
|
||||
|
||||
[scrolling_text.py](scrolling_text.py)
|
||||
|
||||
Display scrolling wisdom, quotes or greetz. You can adjust the brightness with LUX + and -.
|
||||
|
||||
### Today
|
||||
|
||||
[today.py](today.py)
|
||||
|
||||
Calendar example with (optional) NTP synchronization. You can adjust the brightness with LUX + and -, and resync the date by pressing C.
|
||||
|
||||
## Wireless Examples
|
||||
|
||||
These examples need `WIFI_CONFIG.py` and `network_manager.py` (from the `common` directory) to be saved to your Pico W. Open up `WIFI_CONFIG.py` in Thonny to add your wifi details (and save it when you're done).
|
||||
|
||||
- [micropython/examples/common](../../examples/common)
|
||||
|
||||
### Cheerlights History
|
||||
|
||||
[cheerlights_history.py](cheerlights_history.py)
|
||||
|
||||
Updates one pixel every two minutes to display the most recent #Cheerlights colour. Discover the most popular colours over time, or use it as an avant garde (but colourful) 16 hour clock! Find out more about the Cheerlights API at https://cheerlights.com/
|
||||
|
||||
You can adjust the brightness with LUX + and -.
|
||||
|
||||
### Stellar Paint
|
||||
|
||||
[stellar_paint](stellar_paint)
|
||||
|
||||
Draw on your Stellar Unicorn from another device in real time, over wifi!
|
||||
|
||||
This example needs the `micropython-phew` and `microdot` libraries (you can install these using Thonny's 'Tools > Manage Packages').
|
||||
|
||||
### Exchange Ticker
|
||||
|
||||
[exchange_ticker.py](exchange_ticker.py)
|
||||
|
||||
This example uses the Coinbase open API to collect the current exchange rates of various cryptocurrencies.
|
||||
|
||||
Press A to change to a different base exchange currency.
|
||||
|
||||
### HTTP Text
|
||||
|
||||
[http_text](http_text)
|
||||
|
||||
Display scrolling wisdom, quotes or greetz... from another computer or device!
|
||||
|
||||
You can adjust the brightness with LUX + and -.
|
||||
|
||||
Requires `logging.mpy` and `tinyweb` from [micropython/examples/common](../../examples/common) - copy these into the `lib` folder on your Pico W. You'll also need `index.html` to be saved alongside `html_text.py`.
|
||||
|
||||
### Weather
|
||||
|
||||
[weather](weather)
|
||||
|
||||
Display current weather data from the [Open-Meteo](https://open-meteo.com/) weather API.
|
||||
|
||||
## NumPy examples
|
||||
|
||||
[numpy](numpy)
|
||||
|
||||
The examples in the folder use `numpy`-like array functions contained in the `ulab` library for super fast graphical effects.
|
||||
|
||||
## Other Examples
|
||||
|
||||
### Launch (Demo Reel)
|
||||
|
||||
[launch](launch)
|
||||
|
||||
If you want to get the demo reel that Stellar Unicorn ships with back, copy the contents of this `launch` folder to your Pico W.
|
||||
|
||||
## Other Resources
|
||||
|
||||
Here are some cool Stellar Unicorn community projects and resources that you might find useful / inspirational! Note that code at the links below has not been tested by us and we're not able to offer support with it.
|
||||
|
||||
- :link: [Green Energy Display with Stellar Unicorn](https://www.hackster.io/andreas-motzek/clock-and-green-energy-display-with-stellar-unicorn-641dcb)
|
||||
- :link: [stellar-emoji-react - paint emojis from a computer, phone or tablet](https://github.com/chriscareycode/stellar-unicorn/tree/main/stellar-emoji-react)
|
||||
- :link: [stellar-paste - paste images from the clipboard to Stellar Unicorn](https://github.com/chriscareycode/stellar-unicorn/tree/main/stellar-paste)
|
|
@ -0,0 +1,128 @@
|
|||
# This Stellar Unicorn example updates a pixel every two(ish) minutes
|
||||
# to display the most recent #cheerlights colour. Discover the most popular
|
||||
# colours over time, or use it as an avant garde (but colourful) 16 hour clock!
|
||||
# Find out more about the Cheerlights API at https://cheerlights.com/
|
||||
#
|
||||
# To run this example you'll need WIFI_CONFIG.py and network_manager.py from
|
||||
# the pimoroni-pico micropython/examples/common folder
|
||||
|
||||
import WIFI_CONFIG
|
||||
from network_manager import NetworkManager
|
||||
import uasyncio
|
||||
import urequests
|
||||
import time
|
||||
from machine import Timer, Pin
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
URL = 'http://api.thingspeak.com/channels/1417/field/2/last.json'
|
||||
|
||||
UPDATE_INTERVAL = 113 # refresh interval in secs. Be nice to free APIs!
|
||||
# this esoteric number is used so that a column of LEDs equates (approximately) to an hour
|
||||
|
||||
|
||||
def status_handler(mode, status, ip):
|
||||
# reports wifi connection status
|
||||
print(mode, status, ip)
|
||||
print('Connecting to wifi...')
|
||||
if status is not None:
|
||||
if status:
|
||||
print('Wifi connection successful!')
|
||||
else:
|
||||
print('Wifi connection failed!')
|
||||
|
||||
|
||||
def hex_to_rgb(hex):
|
||||
# converts a hex colour code into RGB
|
||||
h = hex.lstrip('#')
|
||||
r, g, b = (int(h[i:i + 2], 16) for i in (0, 2, 4))
|
||||
return r, g, b
|
||||
|
||||
|
||||
def get_data():
|
||||
# open the json file
|
||||
print(f'Requesting URL: {URL}')
|
||||
r = urequests.get(URL)
|
||||
# open the json data
|
||||
j = r.json()
|
||||
print('Data obtained!')
|
||||
r.close()
|
||||
|
||||
# flash the onboard LED after getting data
|
||||
pico_led.value(True)
|
||||
time.sleep(0.2)
|
||||
pico_led.value(False)
|
||||
|
||||
# extract hex colour from the json data
|
||||
hex = j['field2']
|
||||
|
||||
# add the new hex colour to the end of the array
|
||||
colour_array.append(hex)
|
||||
print(f'Colour added to array: {hex}')
|
||||
# remove the oldest colour in the array
|
||||
colour_array.pop(0)
|
||||
update_leds()
|
||||
|
||||
|
||||
def update_leds():
|
||||
# light up the LEDs
|
||||
# this step takes a second, it's doing a lot of hex_to_rgb calculations!
|
||||
print("Updating LEDs...")
|
||||
i = 0
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
r, g, b = hex_to_rgb(colour_array[i])
|
||||
|
||||
current_colour = graphics.create_pen(r, g, b)
|
||||
graphics.set_pen(current_colour)
|
||||
graphics.pixel(x, y)
|
||||
i = i + 1
|
||||
su.update(graphics)
|
||||
print("LEDs updated!")
|
||||
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
# set up the Pico W's onboard LED
|
||||
pico_led = Pin('LED', Pin.OUT)
|
||||
|
||||
current_colour = graphics.create_pen(0, 0, 0)
|
||||
|
||||
# set up an list to store the colours
|
||||
colour_array = ["#000000"] * 1024
|
||||
|
||||
# set up wifi
|
||||
try:
|
||||
network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler)
|
||||
uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK))
|
||||
except Exception as e:
|
||||
print(f'Wifi connection failed! {e}')
|
||||
|
||||
# get the first lot of data
|
||||
get_data()
|
||||
|
||||
# start timer (the timer will call the function to update our data every UPDATE_INTERVAL)
|
||||
timer = Timer(-1)
|
||||
timer.init(period=UPDATE_INTERVAL * 1000, mode=Timer.PERIODIC, callback=lambda t: get_data())
|
||||
|
||||
while True:
|
||||
# adjust brightness with LUX + and -
|
||||
# LEDs take a couple of secs to update, so adjust in big (10%) steps
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.1)
|
||||
update_leds()
|
||||
print(f"Brightness set to {su.get_brightness()}")
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.1)
|
||||
update_leds()
|
||||
print(f"Brightness set to {su.get_brightness()}")
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail)
|
||||
time.sleep(0.001)
|
|
@ -0,0 +1,229 @@
|
|||
# Clock example with NTP synchronization
|
||||
#
|
||||
# Create a secrets.py with your Wifi details to be able to get the time
|
||||
# when the Stellar Unicorn isn't connected to Thonny.
|
||||
#
|
||||
# secrets.py should contain:
|
||||
# WIFI_SSID = "Your WiFi SSID"
|
||||
# WIFI_PASSWORD = "Your WiFi password"
|
||||
#
|
||||
# Clock synchronizes time on start, and resynchronizes if you press the A button
|
||||
|
||||
import time
|
||||
import math
|
||||
import machine
|
||||
import network
|
||||
import ntptime
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
try:
|
||||
from secrets import WIFI_SSID, WIFI_PASSWORD
|
||||
wifi_available = True
|
||||
except ImportError:
|
||||
print("Create secrets.py with your WiFi credentials to get time from NTP")
|
||||
wifi_available = False
|
||||
|
||||
|
||||
# constants for controlling the background colour throughout the day
|
||||
MIDDAY_HUE = 1.1
|
||||
MIDNIGHT_HUE = 0.8
|
||||
HUE_OFFSET = -0.1
|
||||
|
||||
MIDDAY_SATURATION = 1.0
|
||||
MIDNIGHT_SATURATION = 1.0
|
||||
|
||||
MIDDAY_VALUE = 0.8
|
||||
MIDNIGHT_VALUE = 0.3
|
||||
|
||||
|
||||
# create stellar object and graphics surface for drawing
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
# create the rtc object
|
||||
rtc = machine.RTC()
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
|
||||
# set up some pens to use later
|
||||
WHITE = graphics.create_pen(255, 255, 255)
|
||||
BLACK = graphics.create_pen(0, 0, 0)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def from_hsv(h, s, v):
|
||||
i = math.floor(h * 6.0)
|
||||
f = h * 6.0 - i
|
||||
v *= 255.0
|
||||
p = v * (1.0 - s)
|
||||
q = v * (1.0 - f * s)
|
||||
t = v * (1.0 - (1.0 - f) * s)
|
||||
|
||||
i = int(i) % 6
|
||||
if i == 0:
|
||||
return int(v), int(t), int(p)
|
||||
if i == 1:
|
||||
return int(q), int(v), int(p)
|
||||
if i == 2:
|
||||
return int(p), int(v), int(t)
|
||||
if i == 3:
|
||||
return int(p), int(q), int(v)
|
||||
if i == 4:
|
||||
return int(t), int(p), int(v)
|
||||
if i == 5:
|
||||
return int(v), int(p), int(q)
|
||||
|
||||
|
||||
# function for drawing a gradient background
|
||||
def gradient_background(start_hue, start_sat, start_val, end_hue, end_sat, end_val):
|
||||
half_width = width // 2
|
||||
for x in range(0, half_width):
|
||||
hue = ((end_hue - start_hue) * (x / half_width)) + start_hue
|
||||
sat = ((end_sat - start_sat) * (x / half_width)) + start_sat
|
||||
val = ((end_val - start_val) * (x / half_width)) + start_val
|
||||
colour = from_hsv(hue, sat, val)
|
||||
graphics.set_pen(graphics.create_pen(int(colour[0]), int(colour[1]), int(colour[2])))
|
||||
for y in range(0, height):
|
||||
graphics.pixel(x, y)
|
||||
graphics.pixel(width - x - 1, y)
|
||||
|
||||
colour = from_hsv(end_hue, end_sat, end_val)
|
||||
graphics.set_pen(graphics.create_pen(int(colour[0]), int(colour[1]), int(colour[2])))
|
||||
for y in range(0, height):
|
||||
graphics.pixel(half_width, y)
|
||||
|
||||
|
||||
# function for drawing outlined text
|
||||
def outline_text(text, x, y):
|
||||
graphics.set_pen(BLACK)
|
||||
graphics.text(text, x - 1, y - 1, -1, 1)
|
||||
graphics.text(text, x, y - 1, -1, 1)
|
||||
graphics.text(text, x + 1, y - 1, -1, 1)
|
||||
graphics.text(text, x - 1, y, -1, 1)
|
||||
graphics.text(text, x + 1, y, -1, 1)
|
||||
graphics.text(text, x - 1, y + 1, -1, 1)
|
||||
graphics.text(text, x, y + 1, -1, 1)
|
||||
graphics.text(text, x + 1, y + 1, -1, 1)
|
||||
|
||||
graphics.set_pen(WHITE)
|
||||
graphics.text(text, x, y, -1, 1)
|
||||
|
||||
|
||||
# Connect to wifi and synchronize the RTC time from NTP
|
||||
def sync_time():
|
||||
if not wifi_available:
|
||||
return
|
||||
|
||||
# Start connection
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
wlan.active(True)
|
||||
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
|
||||
|
||||
# Wait for connect success or failure
|
||||
max_wait = 100
|
||||
while max_wait > 0:
|
||||
if wlan.status() < 0 or wlan.status() >= 3:
|
||||
break
|
||||
max_wait -= 1
|
||||
print('waiting for connection...')
|
||||
time.sleep(0.2)
|
||||
|
||||
redraw_display_if_reqd()
|
||||
su.update(graphics)
|
||||
|
||||
if max_wait > 0:
|
||||
print("Connected")
|
||||
|
||||
try:
|
||||
ntptime.settime()
|
||||
print("Time set")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
wlan.disconnect()
|
||||
wlan.active(False)
|
||||
|
||||
|
||||
# NTP synchronizes the time to UTC, this allows you to adjust the displayed time
|
||||
# by one hour increments from UTC by pressing the volume up/down buttons
|
||||
#
|
||||
# We use the IRQ method to detect the button presses to avoid incrementing/decrementing
|
||||
# multiple times when the button is held.
|
||||
utc_offset = 0
|
||||
|
||||
up_button = machine.Pin(StellarUnicorn.SWITCH_VOLUME_UP, machine.Pin.IN, machine.Pin.PULL_UP)
|
||||
down_button = machine.Pin(StellarUnicorn.SWITCH_VOLUME_DOWN, machine.Pin.IN, machine.Pin.PULL_UP)
|
||||
|
||||
|
||||
def adjust_utc_offset(pin):
|
||||
global utc_offset
|
||||
if pin == up_button:
|
||||
utc_offset += 1
|
||||
if pin == down_button:
|
||||
utc_offset -= 1
|
||||
|
||||
|
||||
up_button.irq(trigger=machine.Pin.IRQ_FALLING, handler=adjust_utc_offset)
|
||||
down_button.irq(trigger=machine.Pin.IRQ_FALLING, handler=adjust_utc_offset)
|
||||
|
||||
|
||||
year, month, day, wd, hour, minute, second, _ = rtc.datetime()
|
||||
|
||||
last_second = second
|
||||
|
||||
|
||||
# Check whether the RTC time has changed and if so redraw the display
|
||||
def redraw_display_if_reqd():
|
||||
global year, month, day, wd, hour, minute, second, last_second
|
||||
|
||||
year, month, day, wd, hour, minute, second, _ = rtc.datetime()
|
||||
if second != last_second:
|
||||
hour = (hour + utc_offset) % 24
|
||||
time_through_day = (((hour * 60) + minute) * 60) + second
|
||||
percent_through_day = time_through_day / 86400
|
||||
percent_to_midday = 1.0 - ((math.cos(percent_through_day * math.pi * 2) + 1) / 2)
|
||||
print(percent_to_midday)
|
||||
|
||||
hue = ((MIDDAY_HUE - MIDNIGHT_HUE) * percent_to_midday) + MIDNIGHT_HUE
|
||||
sat = ((MIDDAY_SATURATION - MIDNIGHT_SATURATION) * percent_to_midday) + MIDNIGHT_SATURATION
|
||||
val = ((MIDDAY_VALUE - MIDNIGHT_VALUE) * percent_to_midday) + MIDNIGHT_VALUE
|
||||
|
||||
gradient_background(hue, sat, val,
|
||||
hue + HUE_OFFSET, sat, val)
|
||||
|
||||
clock = "{:02}:{:02}:{:02}".format(hour, minute, second)
|
||||
|
||||
# calculate text position so that it is centred
|
||||
w = graphics.measure_text(clock, 1)
|
||||
x = int(width / 2 - w / 2 + 1)
|
||||
y = 5
|
||||
|
||||
outline_text(clock, x, y)
|
||||
|
||||
last_second = second
|
||||
|
||||
|
||||
# set the font
|
||||
graphics.set_font("bitmap6")
|
||||
su.set_brightness(0.5)
|
||||
|
||||
sync_time()
|
||||
|
||||
while True:
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
sync_time()
|
||||
|
||||
redraw_display_if_reqd()
|
||||
|
||||
# update the display
|
||||
su.update(graphics)
|
||||
|
||||
time.sleep(0.01)
|
|
@ -0,0 +1,79 @@
|
|||
import time
|
||||
import random
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
'''
|
||||
Random LEDs blink on and off mimicing the look of a movie
|
||||
super computer doing its work in the eighties.
|
||||
|
||||
You can adjust the brightness with LUX + and -.
|
||||
'''
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
colour = (230, 150, 0)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def setup():
|
||||
global width, height, lifetime, age
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
lifetime = [[0.0 for y in range(height)] for x in range(width)]
|
||||
age = [[0.0 for y in range(height)] for x in range(width)]
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
lifetime[x][y] = 1.0 + random.uniform(0.0, 0.1)
|
||||
age[x][y] = random.uniform(0.0, 1.0) * lifetime[x][y]
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw():
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if age[x][y] < lifetime[x][y] * 0.3:
|
||||
graphics.set_pen(graphics.create_pen(colour[0], colour[1], colour[2]))
|
||||
elif age[x][y] < lifetime[x][y] * 0.5:
|
||||
decay = (lifetime[x][y] * 0.5 - age[x][y]) * 5.0
|
||||
graphics.set_pen(graphics.create_pen(int(decay * colour[0]), int(decay * colour[1]), int(decay * colour[2])))
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(x, y)
|
||||
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def update():
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if age[x][y] >= lifetime[x][y]:
|
||||
age[x][y] = 0.0
|
||||
lifetime[x][y] = 1.0 + random.uniform(0.0, 0.1)
|
||||
|
||||
age[x][y] += 0.025
|
||||
|
||||
|
||||
setup()
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
draw()
|
||||
update()
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail)
|
||||
time.sleep(0.001)
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,148 @@
|
|||
"""
|
||||
This example uses the Coinbase open API to collect the current exchange rates.
|
||||
Use Switch A to change to a different base exchange currency.
|
||||
"""
|
||||
|
||||
import WIFI_CONFIG
|
||||
from network_manager import NetworkManager
|
||||
import uasyncio
|
||||
import urequests
|
||||
import time
|
||||
import math
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
import gc
|
||||
|
||||
URL = 'https://api.coinbase.com/v2/exchange-rates?currency={0}'
|
||||
|
||||
currencies = {"Bitcoin": "BTC", "Ethereun": "ETH", "Pound": "GBP", "Dollar": "USD", "Dogecoin": "DOGE"}
|
||||
currency_keys = list(currencies.keys())
|
||||
|
||||
ref_currency_name = ""
|
||||
currency_name = ""
|
||||
currency_symbol = ""
|
||||
currency_rate = ""
|
||||
rate_keys = []
|
||||
|
||||
# diplay options
|
||||
line_1_line = -2
|
||||
line_2_line = 9
|
||||
line_3_line = 20
|
||||
|
||||
ref_currency_index = 0
|
||||
|
||||
cycles_per_sequence = 120
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
|
||||
# for Handling the wifi connection
|
||||
def status_handler(mode, status, ip):
|
||||
# reports wifi connection status
|
||||
print(mode, status, ip)
|
||||
print('Connecting to wifi...')
|
||||
if status is not None:
|
||||
if status:
|
||||
print('Wifi connection successful!')
|
||||
else:
|
||||
print('Wifi connection failed!')
|
||||
|
||||
|
||||
try:
|
||||
network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler)
|
||||
uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK))
|
||||
except Exception as e:
|
||||
print(f'Wifi connection failed! {e}')
|
||||
|
||||
|
||||
def get_data(currency_selected):
|
||||
|
||||
graphics.set_pen(graphics.create_pen(20, 20, 20))
|
||||
graphics.clear()
|
||||
graphics.set_pen(graphics.create_pen(100, 100, 100))
|
||||
graphics.text("Get", 0, 10, scale=1, spacing=1)
|
||||
graphics.text("data", 8, 16, scale=1, spacing=1)
|
||||
su.update(graphics)
|
||||
gc.collect()
|
||||
# open the json file
|
||||
print('Requesting URL:')
|
||||
print(URL.format(currencies[currency_selected]))
|
||||
r = urequests.get(URL.format(currencies[currency_selected]))
|
||||
gc.collect()
|
||||
# open the json data
|
||||
data_obj = r.json()
|
||||
print('Data obtained!')
|
||||
r.close()
|
||||
return data_obj
|
||||
|
||||
|
||||
def calculate_xpos(length, cycle):
|
||||
cycle_phase = math.cos(math.pi * cycle / (cycles_per_sequence / 2))
|
||||
pos_x = int((-(length / 2) * 10) - (length / 2) * 10 * cycle_phase)
|
||||
return pos_x
|
||||
|
||||
|
||||
def update_display(cycle):
|
||||
|
||||
graphics.set_pen(graphics.create_pen(20, 20, 20))
|
||||
graphics.clear()
|
||||
graphics.set_pen(graphics.create_pen(100, 0, 0))
|
||||
graphics.text(ref_currency_name, calculate_xpos((len(ref_currency_name)), cycle), line_1_line, scale=2, spacing=1)
|
||||
graphics.set_pen(graphics.create_pen(100, 100, 0))
|
||||
if len(currency_symbol) > 3:
|
||||
graphics.text(currency_symbol, calculate_xpos((len(currency_symbol)), cycle), line_2_line, scale=2, spacing=1)
|
||||
else:
|
||||
graphics.text(currency_symbol, 0, line_2_line, scale=2, spacing=1)
|
||||
graphics.set_pen(graphics.create_pen(0, 100, 100))
|
||||
graphics.text(currency_rate, calculate_xpos((len(currency_rate)), cycle), line_3_line, scale=2, spacing=1)
|
||||
|
||||
|
||||
def update_base_currency(index):
|
||||
fetched_data = 0
|
||||
global rates, rate_keys, currency_symbol, currency_rate, ref_currency_name
|
||||
fetched_data = get_data(currency_keys[index])
|
||||
rates = fetched_data['data']['rates']
|
||||
rate_keys = list(rates.keys())
|
||||
currency_symbol = rate_keys[index]
|
||||
currency_rate = str(rates[rate_keys[index]])
|
||||
ref_currency_name = "{0}-{1}".format(currency_keys[index], currencies[currency_keys[index]])
|
||||
gc.collect()
|
||||
|
||||
|
||||
update_base_currency(ref_currency_index)
|
||||
update_display(0)
|
||||
su.update(graphics)
|
||||
cycle_count = 0
|
||||
symbol_index = 0
|
||||
print("Display {0} {1}".format(currency_symbol, currency_rate))
|
||||
|
||||
while 1:
|
||||
if cycle_count > 4 * cycles_per_sequence:
|
||||
cycle_count = 0
|
||||
symbol_index += 1
|
||||
if symbol_index > len(currency_keys):
|
||||
symbol_index = 0
|
||||
print("Display {0} {1}".format(currency_symbol, currency_rate))
|
||||
currency_symbol = rate_keys[symbol_index]
|
||||
currency_rate = rates[rate_keys[symbol_index]]
|
||||
|
||||
if (su.is_pressed(StellarUnicorn.SWITCH_A)):
|
||||
ref_currency_index += 1
|
||||
if (ref_currency_index > len(currency_keys)):
|
||||
ref_currency_index = 0
|
||||
update_base_currency(ref_currency_index)
|
||||
|
||||
if (su.is_pressed(StellarUnicorn.SWITCH_B)):
|
||||
cycle_count = 0
|
||||
symbol_index += 1
|
||||
|
||||
if symbol_index > len(rate_keys):
|
||||
symbol_index = 0
|
||||
currency_symbol = rate_keys[symbol_index]
|
||||
currency_rate = rates[rate_keys[symbol_index]]
|
||||
|
||||
update_display(cycle_count)
|
||||
su.update(graphics)
|
||||
cycle_count += 1
|
||||
time.sleep(0.1)
|
|
@ -0,0 +1,123 @@
|
|||
import time
|
||||
import math
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
'''
|
||||
Displays some text, gradients and colours and demonstrates button use.
|
||||
|
||||
You can adjust the brightness with LUX + and -.
|
||||
'''
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
|
||||
|
||||
def gradient(r, g, b):
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
graphics.set_pen(graphics.create_pen(int((r * x) / 16), int((g * x) / 16), int((b * x) / 16)))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def grid(r, g, b):
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
if (x + y) % 2 == 0:
|
||||
graphics.set_pen(graphics.create_pen(r, g, b))
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def outline_text(text):
|
||||
ms = time.ticks_ms()
|
||||
|
||||
graphics.set_font("bitmap8")
|
||||
v = int((math.sin(ms / 100.0) + 1.0) * 127.0)
|
||||
w = graphics.measure_text(text, 1)
|
||||
|
||||
x = int(16 / 2 - w / 2 + 1)
|
||||
y = 12
|
||||
|
||||
graphics.set_pen(0)
|
||||
graphics.text(text, x - 1, y - 1, -1, 1)
|
||||
graphics.text(text, x, y - 1, -1, 1)
|
||||
graphics.text(text, x + 1, y - 1, -1, 1)
|
||||
graphics.text(text, x - 1, y, -1, 1)
|
||||
graphics.text(text, x + 1, y, -1, 1)
|
||||
graphics.text(text, x - 1, y + 1, -1, 1)
|
||||
graphics.text(text, x, y + 1, -1, 1)
|
||||
graphics.text(text, x + 1, y + 1, -1, 1)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(v, v, v))
|
||||
graphics.text(text, x, y, -1, 1)
|
||||
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
time_ms = time.ticks_ms()
|
||||
test = (time_ms // 1000) % 5
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||
graphics.clear()
|
||||
|
||||
if test == 0:
|
||||
print("grid pattern")
|
||||
grid(255, 255, 255)
|
||||
elif test == 1:
|
||||
print("red gradient")
|
||||
gradient(255, 0, 0)
|
||||
elif test == 2:
|
||||
print("green gradient")
|
||||
gradient(0, 255, 0)
|
||||
elif test == 3:
|
||||
print("blue gradient")
|
||||
gradient(0, 0, 255)
|
||||
elif test == 4:
|
||||
print("white gradient")
|
||||
gradient(255, 255, 255)
|
||||
|
||||
text = ""
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
text = "Button A"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_B):
|
||||
text = "Button B"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_C):
|
||||
text = "Button C"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_D):
|
||||
text = "Button D"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_UP):
|
||||
text = "Louder!"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_DOWN):
|
||||
text = "Quieter"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
text = "Brighter!"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
text = "Darker"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_SLEEP):
|
||||
text = "Zzz... zzz..."
|
||||
|
||||
outline_text(text)
|
||||
|
||||
su.update(graphics)
|
|
@ -0,0 +1,347 @@
|
|||
import gc
|
||||
import time
|
||||
import math
|
||||
from machine import Timer
|
||||
from stellar import StellarUnicorn, Channel
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
'''
|
||||
Displays some text, gradients and colours and demonstrates button use.
|
||||
Also demonstrates some of the audio / synth features.
|
||||
|
||||
- Button A plays a synth tune
|
||||
- Button B plays a solo channel of the synth tune
|
||||
- Button C plays a sinewave (it's frequency can be adjusted with VOL + and -)
|
||||
- Button D plays a second sinewave (it's frequency can be adjusted with LUX + and -)
|
||||
- Sleep button stops the sounds
|
||||
'''
|
||||
|
||||
gc.collect()
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
|
||||
SONG_LENGTH = 384
|
||||
HAT = 20000
|
||||
BASS = 500
|
||||
SNARE = 6000
|
||||
SUB = 50
|
||||
|
||||
melody_notes = (
|
||||
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 220, 0, 196, 0, 147, 0, 175, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
|
||||
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 220, 0, 196, 0, 147, 0, 175, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
|
||||
147, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 247, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 0, 0, 0, 0, 175, 0, 196, 0, 220, 0, 262, 0, 330, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 349, 0, 330, 0, 294, 0, 220, 0, 262, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 247, 0, 0, 0, 0, 0, 0, 0, 247, 0, 262, 0, 294, 0, 392, 0, 440, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
rhythm_notes = (
|
||||
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0,
|
||||
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0,
|
||||
294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 294, 0, 440, 0, 587, 0, 440, 0, 392, 0, 523, 0, 659, 0, 523, 0, 392, 0, 523, 0, 659, 0, 523, 0, 698, 0, 587, 0, 440, 0, 587, 0, 698, 0, 587, 0, 440, 0, 587, 0, 523, 0, 440, 0, 330, 0, 440, 0, 523, 0, 440, 0, 330, 0, 440, 0, 349, 0, 294, 0, 220, 0, 294, 0, 349, 0, 294, 0, 220, 0, 294, 0, 262, 0, 247, 0, 220, 0, 175, 0, 165, 0, 147, 0, 131, 0, 98, 0)
|
||||
|
||||
drum_beats = (
|
||||
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0,
|
||||
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0,
|
||||
BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, BASS, -1, BASS, -1, 0, 0, 0, 0, 0, 0, SNARE, 0, -1, 0, 0, 0, 0, 0)
|
||||
|
||||
hi_hat = (
|
||||
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1,
|
||||
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1,
|
||||
HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1, HAT, -1)
|
||||
|
||||
bass_notes = (
|
||||
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
|
||||
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
|
||||
SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, SUB, -1, SUB, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
notes = [melody_notes, rhythm_notes, drum_beats, hi_hat, bass_notes]
|
||||
channels = [su.synth_channel(i) for i in range(len(notes) + 1)] # Extra channel for tones
|
||||
|
||||
# Configure the synth to play our notes
|
||||
channels[0].configure(waveforms=Channel.TRIANGLE + Channel.SQUARE,
|
||||
attack=0.016,
|
||||
decay=0.168,
|
||||
sustain=0xafff / 65535,
|
||||
release=0.168,
|
||||
volume=10000 / 65535)
|
||||
|
||||
channels[1].configure(waveforms=Channel.SINE + Channel.SQUARE,
|
||||
attack=0.038,
|
||||
decay=0.300,
|
||||
sustain=0,
|
||||
release=0,
|
||||
volume=12000 / 65535)
|
||||
|
||||
channels[2].configure(waveforms=Channel.NOISE,
|
||||
attack=0.005,
|
||||
decay=0.010,
|
||||
sustain=16000 / 65535,
|
||||
release=0.100,
|
||||
volume=18000 / 65535)
|
||||
|
||||
channels[3].configure(waveforms=Channel.NOISE,
|
||||
attack=0.005,
|
||||
decay=0.005,
|
||||
sustain=8000 / 65535,
|
||||
release=0.040,
|
||||
volume=8000 / 65535)
|
||||
|
||||
channels[4].configure(waveforms=Channel.SQUARE,
|
||||
attack=0.010,
|
||||
decay=0.100,
|
||||
sustain=0,
|
||||
release=0.500,
|
||||
volume=12000 / 65535)
|
||||
|
||||
|
||||
def gradient(r, g, b):
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
graphics.set_pen(graphics.create_pen(int((r * x) / 52), int((g * x) / 52), int((b * x) / 52)))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def grid(r, g, b):
|
||||
for y in range(0, height):
|
||||
for x in range(0, width):
|
||||
if (x + y) % 2 == 0:
|
||||
graphics.set_pen(graphics.create_pen(r, g, b))
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def outline_text(text):
|
||||
ms = time.ticks_ms()
|
||||
|
||||
graphics.set_font("bitmap6")
|
||||
v = int((math.sin(ms / 100.0) + 1.0) * 127.0)
|
||||
w = graphics.measure_text(text, 1)
|
||||
|
||||
x = int(16 / 2 - w / 2 + 1)
|
||||
y = 5
|
||||
|
||||
graphics.set_pen(0)
|
||||
graphics.text(text, x - 1, y - 1, -1, 1)
|
||||
graphics.text(text, x, y - 1, -1, 1)
|
||||
graphics.text(text, x + 1, y - 1, -1, 1)
|
||||
graphics.text(text, x - 1, y, -1, 1)
|
||||
graphics.text(text, x + 1, y, -1, 1)
|
||||
graphics.text(text, x - 1, y + 1, -1, 1)
|
||||
graphics.text(text, x, y + 1, -1, 1)
|
||||
graphics.text(text, x + 1, y + 1, -1, 1)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(v, v, v))
|
||||
graphics.text(text, x, y, -1, 1)
|
||||
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
# Vars for storing button state
|
||||
was_a_pressed = False
|
||||
was_b_pressed = False
|
||||
was_c_pressed = False
|
||||
was_d_pressed = False
|
||||
was_z_pressed = False
|
||||
|
||||
# The two frequencies to play
|
||||
tone_a = 0
|
||||
tone_b = 0
|
||||
|
||||
# The current synth beat
|
||||
beat = 0
|
||||
|
||||
|
||||
def next_beat():
|
||||
global beat
|
||||
for i in range(5):
|
||||
if notes[i][beat] > 0:
|
||||
channels[i].frequency(notes[i][beat])
|
||||
channels[i].trigger_attack()
|
||||
elif notes[i][beat] == -1:
|
||||
channels[i].trigger_release()
|
||||
|
||||
beat = (beat + 1) % SONG_LENGTH
|
||||
|
||||
|
||||
def tick(timer):
|
||||
next_beat()
|
||||
|
||||
|
||||
timer = Timer(-1)
|
||||
|
||||
synthing = False
|
||||
|
||||
|
||||
while True:
|
||||
|
||||
time_ms = time.ticks_ms()
|
||||
test = (time_ms // 1000) % 5
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
if not was_a_pressed:
|
||||
channels[0].volume(10000 / 65535)
|
||||
channels[1].volume(12000 / 65535)
|
||||
channels[2].volume(18000 / 65535)
|
||||
channels[3].volume(8000 / 65535)
|
||||
channels[4].volume(12000 / 65535)
|
||||
channels[5].volume(0)
|
||||
|
||||
# If the synth is not already playing, init the first beat
|
||||
if not synthing:
|
||||
beat = 0
|
||||
next_beat()
|
||||
|
||||
su.play_synth()
|
||||
synthing = True
|
||||
timer.init(freq=10, mode=Timer.PERIODIC, callback=tick)
|
||||
|
||||
was_a_pressed = True
|
||||
else:
|
||||
was_a_pressed = False
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_B):
|
||||
if not was_b_pressed:
|
||||
channels[0].volume(0)
|
||||
channels[1].volume(12000 / 65535)
|
||||
channels[2].volume(0)
|
||||
channels[3].volume(0)
|
||||
channels[4].volume(0)
|
||||
channels[5].volume(0)
|
||||
|
||||
# If the synth is not already playing, init the first beat
|
||||
if not synthing:
|
||||
beat = 0
|
||||
next_beat()
|
||||
|
||||
su.play_synth()
|
||||
synthing = True
|
||||
timer.init(freq=10, mode=Timer.PERIODIC, callback=tick)
|
||||
|
||||
was_b_pressed = True
|
||||
else:
|
||||
was_b_pressed = False
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_C):
|
||||
if not was_c_pressed:
|
||||
# Stop synth (if running) and play Tone A
|
||||
timer.deinit()
|
||||
tone_a = 400
|
||||
channels[5].play_tone(tone_a, 0.06)
|
||||
channels[5].volume(12000 / 65535)
|
||||
|
||||
su.play_synth()
|
||||
synthing = False
|
||||
|
||||
was_c_pressed = True
|
||||
else:
|
||||
was_c_pressed = False
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_D):
|
||||
if not was_d_pressed:
|
||||
# Stop synth (if running) and play Tone B
|
||||
timer.deinit()
|
||||
tone_b = 600
|
||||
|
||||
channels[5].play_tone(tone_b, 0.06, attack=0.5)
|
||||
channels[5].volume(12000 / 65535)
|
||||
|
||||
su.play_synth()
|
||||
synthing = False
|
||||
|
||||
was_d_pressed = True
|
||||
else:
|
||||
was_d_pressed = False
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
if tone_b > 0: # Zero means tone not playing
|
||||
# Increase Tone B
|
||||
tone_b = min(tone_b + 10, 20000)
|
||||
channels[5].frequency(tone_b)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
if tone_b > 0: # Zero means tone not playing
|
||||
# Decrease Tone B
|
||||
tone_b = max(tone_b - 10, 10)
|
||||
channels[5].frequency(max(tone_b, 10))
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_UP):
|
||||
if tone_a > 0: # Zero means tone not playing
|
||||
# Increase Tone A
|
||||
tone_a = min(tone_a + 10, 20000)
|
||||
channels[5].frequency(tone_a)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_DOWN):
|
||||
if tone_a > 0: # Zero means tone not playing
|
||||
# Decrease Tone A
|
||||
tone_a = max(tone_a - 10, 10)
|
||||
channels[5].frequency(tone_a)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_SLEEP):
|
||||
if not was_z_pressed:
|
||||
# Stop synth and both tones
|
||||
tone_a = 0
|
||||
tone_b = 0
|
||||
su.stop_playing()
|
||||
timer.deinit()
|
||||
synthing = False
|
||||
|
||||
was_z_pressed = True
|
||||
else:
|
||||
was_z_pressed = False
|
||||
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||
graphics.clear()
|
||||
|
||||
if test == 0:
|
||||
# print("grid pattern")
|
||||
grid(255, 255, 255)
|
||||
elif test == 1:
|
||||
# print("red gradient")
|
||||
gradient(255, 0, 0)
|
||||
elif test == 2:
|
||||
# print("green gradient")
|
||||
gradient(0, 255, 0)
|
||||
elif test == 3:
|
||||
# print("blue gradient")
|
||||
gradient(0, 0, 255)
|
||||
elif test == 4:
|
||||
# print("white gradient")
|
||||
gradient(255, 255, 255)
|
||||
|
||||
text = ""
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
text = "PlaySyn"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_B):
|
||||
text = "SoloSyn"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_C):
|
||||
text = "Tone A"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_D):
|
||||
text = "Tone B"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_UP):
|
||||
text = "RaiseA"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_DOWN):
|
||||
text = "LowerA"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
text = "RaiseB"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
text = "LowerB"
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_SLEEP):
|
||||
text = "Stop"
|
||||
|
||||
outline_text(text)
|
||||
|
||||
su.update(graphics)
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail
|
||||
time.sleep(0.001)
|
|
@ -0,0 +1,102 @@
|
|||
import time
|
||||
import random
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
'''
|
||||
A pretty, procedural fire effect.
|
||||
|
||||
You can adjust the brightness with LUX + and -.
|
||||
'''
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
fire_colours = [graphics.create_pen(0, 0, 0),
|
||||
graphics.create_pen(20, 20, 20),
|
||||
graphics.create_pen(180, 30, 0),
|
||||
graphics.create_pen(220, 160, 0),
|
||||
graphics.create_pen(255, 255, 180)]
|
||||
|
||||
|
||||
@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
|
||||
|
||||
# clear the bottom row and then add a new fire seed to it
|
||||
for x in range(width):
|
||||
_heat[x][height - 1] = 0.0
|
||||
_heat[x][height - 2] = 0.0
|
||||
|
||||
for c in range(fire_spawns):
|
||||
x = random.randint(0, width - 4) + 2
|
||||
_heat[x + 0][height - 1] = 1.0
|
||||
_heat[x + 1][height - 1] = 1.0
|
||||
_heat[x - 1][height - 1] = 1.0
|
||||
_heat[x + 0][height - 2] = 1.0
|
||||
_heat[x + 1][height - 2] = 1.0
|
||||
_heat[x - 1][height - 2] = 1.0
|
||||
|
||||
factor = damping_factor / 5.0
|
||||
for y in range(0, height - 2):
|
||||
for x in range(1, width - 1):
|
||||
_heat[x][y] += _heat[x][y + 1] + _heat[x][y + 2] + _heat[x - 1][y + 1] + _heat[x + 1][y + 1]
|
||||
_heat[x][y] *= factor
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw():
|
||||
# take local references as it's quicker than accessing the global
|
||||
# and we access it a lot in this method
|
||||
_graphics = graphics
|
||||
_heat = heat
|
||||
_set_pen = graphics.set_pen
|
||||
_pixel = graphics.pixel
|
||||
_fire_colours = fire_colours
|
||||
|
||||
for y in range(StellarUnicorn.HEIGHT):
|
||||
for x in range(StellarUnicorn.WIDTH):
|
||||
value = _heat[x + 1][y]
|
||||
if value < 0.15:
|
||||
_set_pen(_fire_colours[0])
|
||||
elif value < 0.25:
|
||||
_set_pen(_fire_colours[1])
|
||||
elif value < 0.35:
|
||||
_set_pen(_fire_colours[2])
|
||||
elif value < 0.45:
|
||||
_set_pen(_fire_colours[3])
|
||||
else:
|
||||
_set_pen(_fire_colours[4])
|
||||
_pixel(x, y)
|
||||
|
||||
su.update(_graphics)
|
||||
|
||||
|
||||
width = StellarUnicorn.WIDTH + 2
|
||||
height = StellarUnicorn.HEIGHT + 4
|
||||
heat = [[0.0 for y in range(height)] for x in range(width)]
|
||||
fire_spawns = 5
|
||||
damping_factor = 0.97
|
||||
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
update()
|
||||
draw()
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail)
|
||||
time.sleep(0.001)
|
|
@ -0,0 +1,190 @@
|
|||
import time
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
import WIFI_CONFIG
|
||||
from network_manager import NetworkManager
|
||||
import uasyncio as asyncio
|
||||
import uasyncio.core
|
||||
from tinyweb.server import webserver
|
||||
|
||||
'''
|
||||
Display scrolling wisdom, quotes or greetz... from the internetz!
|
||||
|
||||
You can adjust the brightness with LUX + and -.
|
||||
|
||||
Requires network_manager.py , WIFI_CONFIG.py, logging.mpy and tinyweb from micropython/examples/common
|
||||
You'll also need index.html to be saved alongside this file.
|
||||
'''
|
||||
|
||||
# Server Settings
|
||||
host = "0.0.0.0"
|
||||
port = 80
|
||||
|
||||
|
||||
def convert_colour(colour_str):
|
||||
colour_str = colour_str.split(',')
|
||||
print(colour_str)
|
||||
return colour_str[0], colour_str[1], colour_str[2]
|
||||
|
||||
|
||||
class text:
|
||||
|
||||
def get(self, data):
|
||||
global MESSAGE, MESSAGE_COLOUR, BACKGROUND_COLOUR
|
||||
print(data)
|
||||
if 'text' in data.keys():
|
||||
MESSAGE = data['text']
|
||||
if 'colourfg' in data.keys():
|
||||
MESSAGE_COLOUR = convert_colour(data['colourfg'])
|
||||
if 'colourbg' in data.keys():
|
||||
BACKGROUND_COLOUR = convert_colour(data['colourbg'])
|
||||
return {'message': 'text updated'}, 201
|
||||
|
||||
def post(self, data):
|
||||
|
||||
return {'message': 'text updated'}, 201
|
||||
|
||||
|
||||
def status_handler(mode, status, ip):
|
||||
global MESSAGE
|
||||
print("Network: {}".format(WIFI_CONFIG.SSID))
|
||||
status_text = "Connecting..."
|
||||
if status is not None:
|
||||
if status:
|
||||
status_text = "Connection successful!"
|
||||
else:
|
||||
status_text = "Connection failed!"
|
||||
|
||||
print(status_text)
|
||||
print("IP: {}".format(ip))
|
||||
MESSAGE = "{}".format(ip)
|
||||
|
||||
|
||||
# Create web server application
|
||||
app = webserver()
|
||||
|
||||
|
||||
# Index page
|
||||
@app.route('/')
|
||||
async def index(request, response):
|
||||
# Send actual HTML page
|
||||
await response.send_file('index.html', content_type='text/html')
|
||||
|
||||
|
||||
# HTTP redirection
|
||||
@app.route('/redirect')
|
||||
async def redirect(request, response):
|
||||
# Start HTTP response with content-type text/html
|
||||
await response.redirect('/')
|
||||
|
||||
# constants for controlling scrolling text
|
||||
PADDING = 5
|
||||
MESSAGE_COLOUR = (255, 255, 255)
|
||||
OUTLINE_COLOUR = (0, 0, 0)
|
||||
BACKGROUND_COLOUR = (10, 0, 96)
|
||||
MESSAGE = "Connecting"
|
||||
HOLD_TIME = 2.0
|
||||
STEP_TIME = 0.075
|
||||
|
||||
# create galactic object and graphics surface for drawing
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
|
||||
|
||||
# function for drawing outlined text
|
||||
def outline_text(text, x, y):
|
||||
graphics.set_pen(graphics.create_pen(int(OUTLINE_COLOUR[0]), int(OUTLINE_COLOUR[1]), int(OUTLINE_COLOUR[2])))
|
||||
graphics.text(text, x - 1, y - 1, -1, 1)
|
||||
graphics.text(text, x, y - 1, -1, 1)
|
||||
graphics.text(text, x + 1, y - 1, -1, 1)
|
||||
graphics.text(text, x - 1, y, -1, 1)
|
||||
graphics.text(text, x + 1, y, -1, 1)
|
||||
graphics.text(text, x - 1, y + 1, -1, 1)
|
||||
graphics.text(text, x, y + 1, -1, 1)
|
||||
graphics.text(text, x + 1, y + 1, -1, 1)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(int(MESSAGE_COLOUR[0]), int(MESSAGE_COLOUR[1]), int(MESSAGE_COLOUR[2])))
|
||||
graphics.text(text, x, y, -1, 1)
|
||||
|
||||
|
||||
def run():
|
||||
# Setup wifi
|
||||
network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler)
|
||||
|
||||
app.add_resource(text, '/update')
|
||||
|
||||
# Connect to Wifi network
|
||||
asyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK))
|
||||
while (not network_manager.isconnected()):
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
# Start wifi connection
|
||||
run()
|
||||
|
||||
|
||||
async def message_update():
|
||||
global MESSAGE
|
||||
last_time = time.ticks_ms()
|
||||
STATE_PRE_SCROLL = 0
|
||||
STATE_SCROLLING = 1
|
||||
STATE_POST_SCROLL = 2
|
||||
|
||||
shift = 0
|
||||
state = STATE_PRE_SCROLL
|
||||
|
||||
# set the font
|
||||
graphics.set_font("bitmap8")
|
||||
|
||||
# calculate the message width so scrolling can happen
|
||||
msg_width = graphics.measure_text(MESSAGE, 1)
|
||||
while 1:
|
||||
|
||||
msg_width = graphics.measure_text(MESSAGE, 1)
|
||||
time_ms = time.ticks_ms()
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
if state == STATE_PRE_SCROLL and time_ms - last_time > HOLD_TIME * 1000:
|
||||
if msg_width + PADDING * 2 >= width:
|
||||
state = STATE_SCROLLING
|
||||
last_time = time_ms
|
||||
|
||||
if state == STATE_SCROLLING and time_ms - last_time > STEP_TIME * 1000:
|
||||
shift += 1
|
||||
if shift >= (msg_width + PADDING * 2) - width - 1:
|
||||
state = STATE_POST_SCROLL
|
||||
last_time = time_ms
|
||||
|
||||
if state == STATE_POST_SCROLL and time_ms - last_time > HOLD_TIME * 1000:
|
||||
state = STATE_PRE_SCROLL
|
||||
shift = 0
|
||||
last_time = time_ms
|
||||
|
||||
graphics.set_pen(graphics.create_pen(int(BACKGROUND_COLOUR[0]), int(BACKGROUND_COLOUR[1]), int(BACKGROUND_COLOUR[2])))
|
||||
graphics.clear()
|
||||
|
||||
outline_text(MESSAGE, x=PADDING - shift, y=11)
|
||||
|
||||
# update the display
|
||||
su.update(graphics)
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail)
|
||||
await asyncio.sleep(0.001)
|
||||
|
||||
|
||||
# The following is required to run both the web server and the scrolling text coherently
|
||||
app._server_coro = app._tcp_server(host, port, app.backlog)
|
||||
loop = asyncio.get_event_loop()
|
||||
t1 = loop.create_task(message_update())
|
||||
t2 = loop.create_task(app._server_coro)
|
||||
loop.run_forever()
|
|
@ -0,0 +1,102 @@
|
|||
<http>
|
||||
<head>
|
||||
<style>
|
||||
h1 {
|
||||
align-content: center;
|
||||
color: rgb(192, 192, 214);
|
||||
margin-left: 20px;
|
||||
background-color: darkmagenta;
|
||||
font-size: xxx-large;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var getUrl = window.location;
|
||||
var baseUrl = getUrl.protocol + "//" + getUrl.host;
|
||||
var displayText;
|
||||
var colourBF ="#000000";
|
||||
var colourFG ="#000000";
|
||||
|
||||
function convertHexToRgb(hex) {
|
||||
|
||||
// Convert the first 2 characters to hexadecimal
|
||||
var r = parseInt(hex.substring(1, 3), 16),
|
||||
|
||||
// Convert the middle 2 characters to hexadecimal
|
||||
g = parseInt(hex.substring(3, 5), 16),
|
||||
|
||||
// Convert the last 2 characters to hexadecimal
|
||||
b = parseInt(hex.substring(5, 7), 16);
|
||||
|
||||
// append them all
|
||||
return r + ", " + g + ", "
|
||||
+ b ;
|
||||
}
|
||||
|
||||
function updateCU(comm,val){
|
||||
console.log(typeof(comm))
|
||||
console.log(typeof(val))
|
||||
console.log(comm, val)
|
||||
fetch(baseUrl+"/update" + "?"+comm+"="+String(val))
|
||||
.then(response => {
|
||||
// indicates whether the response is successful (status code 200-299) or not
|
||||
if (!response.ok) {
|
||||
throw new Error(`Request failed with status ${reponse.status}`)
|
||||
}
|
||||
return response.json()
|
||||
})
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
|
||||
})
|
||||
.catch(error => console.log(error))
|
||||
}
|
||||
function updateText(){
|
||||
displayText = document.getElementById("inputtxt").value;
|
||||
colourFG = convertHexToRgb(document.getElementById("inputfgcolour").value);
|
||||
colourBG = convertHexToRgb(document.getElementById("inputbgcolour").value);
|
||||
updateCU("colourfg", colourFG);
|
||||
updateCU("text", displayText);
|
||||
|
||||
updateCU("colourbg", colourBG);
|
||||
}
|
||||
function updateColour(){
|
||||
colourFG = document.getElementById('inputfgcolour').value;
|
||||
colourBG = document.getElementById('inputbgcolour').value;
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="title">Stellar Unicorn Web Text</h1>
|
||||
<center>
|
||||
<label for="inputtxt"> Enter text to display:</label>
|
||||
<br>
|
||||
|
||||
<input type="text" id="inputtxt" name="inputtxt" required
|
||||
minlength="4" size="16">
|
||||
<br>
|
||||
<label for="inputfgcolour"> Text Colour:</label>
|
||||
<br>
|
||||
|
||||
<input type="color" id="inputfgcolour" name="inputfgcolour" required
|
||||
minlength="4" maxlength="30" size="16" onchange="updateColour()" value='#000000'>
|
||||
<br>
|
||||
<label for="inputbgcolour"> Background Colour:</label>
|
||||
<br>
|
||||
|
||||
<input type="color" id="inputbgcolour" name="inputbgcolour" required
|
||||
minlength="4" maxlength="30" size="16" onchange="updateColour()" value='#000000'>
|
||||
<br>
|
||||
<button class="favorite styled"
|
||||
onclick="updateText()"
|
||||
type="button">
|
||||
Update
|
||||
</button>
|
||||
<p>
|
||||
Please type in what you wish to be displayed on the Stellar Unicorn and whe you are ready hit update to update the display
|
||||
</p>
|
||||
|
||||
</center>
|
||||
</body>
|
||||
</http>
|
|
@ -0,0 +1,80 @@
|
|||
import random
|
||||
from stellar import StellarUnicorn
|
||||
|
||||
graphics = None
|
||||
palette = None
|
||||
|
||||
# setup heat value buffer and fire parameters
|
||||
width = StellarUnicorn.WIDTH + 2
|
||||
height = StellarUnicorn.HEIGHT + 4
|
||||
heat = [[0.0 for y in range(height)] for x in range(width)]
|
||||
fire_spawns = 5
|
||||
damping_factor = 0.97
|
||||
|
||||
|
||||
def init():
|
||||
# a palette of five firey colours (white, yellow, orange, red, smoke)
|
||||
global palette
|
||||
palette = [
|
||||
graphics.create_pen(0, 0, 0),
|
||||
graphics.create_pen(20, 20, 20),
|
||||
graphics.create_pen(180, 30, 0),
|
||||
graphics.create_pen(220, 160, 0),
|
||||
graphics.create_pen(255, 255, 180)
|
||||
]
|
||||
|
||||
|
||||
# returns the palette entry for a given heat value
|
||||
@micropython.native # noqa: F821
|
||||
def pen_from_value(value):
|
||||
if value < 0.15:
|
||||
return palette[0]
|
||||
elif value < 0.25:
|
||||
return palette[1]
|
||||
elif value < 0.35:
|
||||
return palette[2]
|
||||
elif value < 0.45:
|
||||
return palette[3]
|
||||
return palette[4]
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw():
|
||||
# clear the the rows off the bottom of the display
|
||||
for x in range(width):
|
||||
heat[x][height - 1] = 0.0
|
||||
heat[x][height - 2] = 0.0
|
||||
|
||||
# add new fire spawns
|
||||
for c in range(fire_spawns):
|
||||
x = random.randint(0, width - 4) + 2
|
||||
heat[x + 0][height - 1] = 1.0
|
||||
heat[x + 1][height - 1] = 1.0
|
||||
heat[x - 1][height - 1] = 1.0
|
||||
heat[x + 0][height - 2] = 1.0
|
||||
heat[x + 1][height - 2] = 1.0
|
||||
heat[x - 1][height - 2] = 1.0
|
||||
|
||||
# average and damp out each value to create rising flame effect
|
||||
for y in range(0, height - 2):
|
||||
for x in range(1, width - 1):
|
||||
# update this pixel by averaging the below pixels
|
||||
average = (
|
||||
heat[x][y] + heat[x][y + 1] + heat[x][y + 2] + heat[x - 1][y + 1] + heat[x + 1][y + 1]
|
||||
) / 5.0
|
||||
|
||||
# damping factor to ensure flame tapers out towards the top of the displays
|
||||
average *= damping_factor
|
||||
|
||||
# update the heat map with our newly averaged value
|
||||
heat[x][y] = average
|
||||
|
||||
# render the heat values to the graphics buffer
|
||||
for y in range(StellarUnicorn.HEIGHT):
|
||||
for x in range(StellarUnicorn.WIDTH):
|
||||
graphics.set_pen(pen_from_value(heat[x + 1][y]))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
|
||||
def test():
|
||||
print("A")
|
|
@ -0,0 +1,113 @@
|
|||
import time
|
||||
import machine
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
# overclock to 200Mhz
|
||||
machine.freq(200000000)
|
||||
|
||||
# create stellar object and graphics surface for drawing
|
||||
stellar = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
brightness = 0.5
|
||||
|
||||
|
||||
# returns the id of the button that is currently pressed or
|
||||
# None if none are
|
||||
def pressed():
|
||||
if stellar.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
return StellarUnicorn.SWITCH_A
|
||||
if stellar.is_pressed(StellarUnicorn.SWITCH_B):
|
||||
return StellarUnicorn.SWITCH_B
|
||||
if stellar.is_pressed(StellarUnicorn.SWITCH_C):
|
||||
return StellarUnicorn.SWITCH_C
|
||||
if stellar.is_pressed(StellarUnicorn.SWITCH_D):
|
||||
return StellarUnicorn.SWITCH_D
|
||||
return None
|
||||
|
||||
|
||||
# wait for a button to be pressed and load that effect
|
||||
while True:
|
||||
graphics.set_font("bitmap6")
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||
graphics.clear()
|
||||
graphics.set_pen(graphics.create_pen(155, 155, 155))
|
||||
graphics.text("PRESS", 3, 6, -1, 1)
|
||||
graphics.text("A B C OR D!", 5, 14, 16, 1, 0)
|
||||
|
||||
# brightness up/down
|
||||
if stellar.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
brightness += 0.01
|
||||
if stellar.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
brightness -= 0.01
|
||||
brightness = max(min(brightness, 1.0), 0.0)
|
||||
|
||||
stellar.set_brightness(brightness)
|
||||
stellar.update(graphics)
|
||||
|
||||
if pressed() == StellarUnicorn.SWITCH_A:
|
||||
import fire as effect
|
||||
break
|
||||
if pressed() == StellarUnicorn.SWITCH_B:
|
||||
import supercomputer as effect # noqa: F811
|
||||
break
|
||||
if pressed() == StellarUnicorn.SWITCH_C:
|
||||
import rainbow as effect # noqa: F811
|
||||
break
|
||||
if pressed() == StellarUnicorn.SWITCH_D:
|
||||
import today as effect # noqa: F811
|
||||
break
|
||||
|
||||
# pause for a moment
|
||||
time.sleep(0.01)
|
||||
|
||||
# wait until all buttons are released
|
||||
while pressed() is not None:
|
||||
time.sleep(0.1)
|
||||
|
||||
effect.graphics = graphics
|
||||
effect.init()
|
||||
|
||||
sleep = False
|
||||
was_sleep_pressed = False
|
||||
|
||||
|
||||
# wait
|
||||
while True:
|
||||
# if A, B, C, or D are pressed then reset
|
||||
if pressed() is not None:
|
||||
machine.reset()
|
||||
|
||||
sleep_pressed = stellar.is_pressed(StellarUnicorn.SWITCH_SLEEP)
|
||||
if sleep_pressed and not was_sleep_pressed:
|
||||
sleep = not sleep
|
||||
|
||||
was_sleep_pressed = sleep_pressed
|
||||
|
||||
if sleep:
|
||||
# fade out if screen not off
|
||||
stellar.set_brightness(stellar.get_brightness() - 0.01)
|
||||
|
||||
if stellar.get_brightness() > 0.0:
|
||||
effect.draw()
|
||||
|
||||
# update the display
|
||||
stellar.update(graphics)
|
||||
else:
|
||||
effect.draw()
|
||||
|
||||
# update the display
|
||||
stellar.update(graphics)
|
||||
|
||||
# brightness up/down
|
||||
if stellar.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
brightness += 0.01
|
||||
if stellar.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
brightness -= 0.01
|
||||
brightness = max(min(brightness, 1.0), 0.0)
|
||||
|
||||
stellar.set_brightness(brightness)
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail
|
||||
time.sleep(0.001)
|
|
@ -0,0 +1,59 @@
|
|||
import math
|
||||
from stellar import StellarUnicorn
|
||||
|
||||
graphics = None
|
||||
palette = None
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def from_hsv(h, s, v):
|
||||
i = math.floor(h * 6.0)
|
||||
f = h * 6.0 - i
|
||||
v *= 255.0
|
||||
p = v * (1.0 - s)
|
||||
q = v * (1.0 - f * s)
|
||||
t = v * (1.0 - (1.0 - f) * s)
|
||||
|
||||
i = int(i) % 6
|
||||
if i == 0:
|
||||
return int(v), int(t), int(p)
|
||||
if i == 1:
|
||||
return int(q), int(v), int(p)
|
||||
if i == 2:
|
||||
return int(p), int(v), int(t)
|
||||
if i == 3:
|
||||
return int(p), int(q), int(v)
|
||||
if i == 4:
|
||||
return int(t), int(p), int(v)
|
||||
if i == 5:
|
||||
return int(v), int(p), int(q)
|
||||
|
||||
|
||||
phase = 0
|
||||
hue_map = [from_hsv(x / width, 1.0, 1.0) for x in range(width)]
|
||||
hue_offset = 0.0
|
||||
stripe_width = 3.0
|
||||
speed = 5.0
|
||||
|
||||
|
||||
def init():
|
||||
pass
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw():
|
||||
global hue_offset, phase
|
||||
|
||||
phase += speed
|
||||
|
||||
phase_percent = phase / 15
|
||||
for x in range(width):
|
||||
colour = hue_map[int((x + (hue_offset * width)) % width)]
|
||||
for y in range(height):
|
||||
v = ((math.sin((x + y) / stripe_width + phase_percent) + 1.5) / 2.5)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(int(colour[0] * v), int(colour[1] * v), int(colour[2] * v)))
|
||||
graphics.pixel(x, y)
|
|
@ -0,0 +1,40 @@
|
|||
import random
|
||||
from stellar import StellarUnicorn
|
||||
|
||||
graphics = None
|
||||
|
||||
colour = (230, 150, 0)
|
||||
|
||||
|
||||
def init():
|
||||
global width, height, lifetime, age
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
lifetime = [[0.0 for y in range(height)] for x in range(width)]
|
||||
age = [[0.0 for y in range(height)] for x in range(width)]
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
lifetime[x][y] = 1.0 + random.uniform(0.0, 0.1)
|
||||
age[x][y] = random.uniform(0.0, 1.0) * lifetime[x][y]
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw():
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if age[x][y] >= lifetime[x][y]:
|
||||
age[x][y] = 0.0
|
||||
lifetime[x][y] = 1.0 + random.uniform(0.0, 0.1)
|
||||
|
||||
age[x][y] += 0.025
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if age[x][y] < lifetime[x][y] * 0.3:
|
||||
graphics.set_pen(graphics.create_pen(colour[0], colour[1], colour[2]))
|
||||
elif age[x][y] < lifetime[x][y] * 0.5:
|
||||
decay = (lifetime[x][y] * 0.5 - age[x][y]) * 5.0
|
||||
graphics.set_pen(graphics.create_pen(int(decay * colour[0]), int(decay * colour[1]), int(decay * colour[2])))
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(x, y)
|
|
@ -0,0 +1,100 @@
|
|||
import time
|
||||
import network
|
||||
import ntptime
|
||||
import machine
|
||||
|
||||
# You will need to create or update the file secrets.py with your network credentials using Thonny
|
||||
# in order for the example to update using the NTP.
|
||||
|
||||
# secrets.py should contain:
|
||||
# WIFI_SSID = ""
|
||||
# WIFI_PASSWORD = ""
|
||||
|
||||
try:
|
||||
from secrets import WIFI_SSID, WIFI_PASSWORD
|
||||
except ImportError:
|
||||
print("Create secrets.py with your WiFi credentials")
|
||||
|
||||
graphics = None
|
||||
|
||||
WIDTH = 16 # StellarUnicorn.WIDTH
|
||||
HEIGHT = 16 # StellarUnicorn.HEIGHT
|
||||
|
||||
rtc = machine.RTC()
|
||||
|
||||
DAYS = ["Mon", "Tue", "Wed", "Thur", "Fri", "Sat", "Sun"]
|
||||
|
||||
# Enable the Wireless
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
wlan.active(True)
|
||||
|
||||
|
||||
def network_connect(SSID, PSK):
|
||||
|
||||
# Number of attempts to make before timeout
|
||||
max_wait = 5
|
||||
|
||||
# Sets the Wireless LED pulsing and attempts to connect to your local network.
|
||||
print("connecting...")
|
||||
wlan.config(pm=0xa11140) # Turn WiFi power saving off for some slow APs
|
||||
wlan.connect(SSID, PSK)
|
||||
|
||||
while max_wait > 0:
|
||||
if wlan.status() < 0 or wlan.status() >= 3:
|
||||
break
|
||||
max_wait -= 1
|
||||
print('waiting for connection...')
|
||||
time.sleep(1)
|
||||
|
||||
# Handle connection error. Switches the Warn LED on.
|
||||
if wlan.status() != 3:
|
||||
print("Unable to connect. Attempting connection again")
|
||||
|
||||
|
||||
# Function to sync the Pico RTC using NTP
|
||||
def sync_time():
|
||||
|
||||
try:
|
||||
network_connect(WIFI_SSID, WIFI_PASSWORD)
|
||||
except NameError:
|
||||
print("Create secrets.py with your WiFi credentials")
|
||||
|
||||
if wlan.status() < 0 or wlan.status() >= 3:
|
||||
try:
|
||||
ntptime.settime()
|
||||
except OSError:
|
||||
print("Unable to sync with NTP server. Check network and try again.")
|
||||
|
||||
|
||||
def init():
|
||||
|
||||
sync_time()
|
||||
|
||||
|
||||
def draw():
|
||||
|
||||
# Pens
|
||||
RED = graphics.create_pen(120, 0, 0)
|
||||
WHITE = graphics.create_pen(255, 255, 255)
|
||||
|
||||
current_t = rtc.datetime()
|
||||
|
||||
# Set the pen to Red and clear the screen.
|
||||
graphics.set_pen(WHITE)
|
||||
graphics.clear()
|
||||
|
||||
# Measures the length of the text to help us with centring later.
|
||||
day_length = graphics.measure_text(DAYS[current_t[3]], 1)
|
||||
date_length = graphics.measure_text(str(current_t[2]), 3)
|
||||
|
||||
graphics.set_font("bitmap6")
|
||||
graphics.set_pen(RED)
|
||||
graphics.rectangle(0, 0, WIDTH, 7)
|
||||
graphics.set_pen(WHITE)
|
||||
graphics.text(DAYS[current_t[3]], (WIDTH // 2) - (day_length // 2) - 1, 0, 16, 1)
|
||||
|
||||
graphics.set_pen(RED)
|
||||
graphics.set_font("bitmap8")
|
||||
graphics.text(str(current_t[2]), (WIDTH // 2) - (date_length // 2) + 1, 9, 16, 3)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
|
@ -0,0 +1,151 @@
|
|||
import time
|
||||
import random
|
||||
import math
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
'''
|
||||
A 70s-tastic, procedural rainbow lava lamp.
|
||||
|
||||
You can adjust the brightness with LUX + and -.
|
||||
'''
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
blob_count = 10
|
||||
|
||||
|
||||
class Blob():
|
||||
def __init__(self):
|
||||
self.x = float(random.randint(0, width - 1))
|
||||
self.y = float(random.randint(0, height - 1))
|
||||
self.r = (float(random.randint(0, 40)) / 10.0) + 5.0
|
||||
self.dx = (float(random.randint(0, 2)) / 10.0) - 0.1
|
||||
self.dy = (float(random.randint(0, 2)) / 10.0) - 0.05 # positive bias
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def setup_portrait():
|
||||
global width, height, liquid, blobs
|
||||
width = StellarUnicorn.HEIGHT
|
||||
height = StellarUnicorn.WIDTH
|
||||
liquid = [[0.0 for y in range(height)] for x in range(width)]
|
||||
blobs = [Blob() for i in range(blob_count)]
|
||||
|
||||
|
||||
hue = 0.0
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def from_hsv(h, s, v):
|
||||
i = math.floor(h * 6.0)
|
||||
f = h * 6.0 - i
|
||||
v *= 255.0
|
||||
p = v * (1.0 - s)
|
||||
q = v * (1.0 - f * s)
|
||||
t = v * (1.0 - (1.0 - f) * s)
|
||||
|
||||
i = int(i) % 6
|
||||
if i == 0:
|
||||
return graphics.create_pen(int(v), int(t), int(p))
|
||||
if i == 1:
|
||||
return graphics.create_pen(int(q), int(v), int(p))
|
||||
if i == 2:
|
||||
return graphics.create_pen(int(p), int(v), int(t))
|
||||
if i == 3:
|
||||
return graphics.create_pen(int(p), int(q), int(v))
|
||||
if i == 4:
|
||||
return graphics.create_pen(int(t), int(p), int(v))
|
||||
if i == 5:
|
||||
return graphics.create_pen(int(v), int(p), int(q))
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def update_liquid():
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
liquid[x][y] = 0.0
|
||||
|
||||
for blob in blobs:
|
||||
r_sq = blob.r * blob.r
|
||||
blob_y_range = range(max(math.floor(blob.y - blob.r), 0),
|
||||
min(math.ceil(blob.y + blob.r), height))
|
||||
blob_x_range = range(max(math.floor(blob.x - blob.r), 0),
|
||||
min(math.ceil(blob.x + blob.r), width))
|
||||
|
||||
for y in blob_y_range:
|
||||
for x in blob_x_range:
|
||||
x_diff = x - blob.x
|
||||
y_diff = y - blob.y
|
||||
d_sq = x_diff * x_diff + y_diff * y_diff
|
||||
if d_sq <= r_sq:
|
||||
liquid[x][y] += 1.0 - (d_sq / r_sq)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def move_blobs():
|
||||
for blob in blobs:
|
||||
blob.x += blob.dx
|
||||
blob.y += blob.dy
|
||||
|
||||
if blob.x < 0.0 or blob.x >= float(width):
|
||||
blob.dx = 0.0 - blob.dx
|
||||
|
||||
if blob.y < 0.0 or blob.y >= float(height):
|
||||
blob.dy = 0.0 - blob.dy
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw_portrait():
|
||||
global hue
|
||||
hue += 0.001
|
||||
|
||||
dark = from_hsv(hue, 1.0, 0.3)
|
||||
mid = from_hsv(hue, 1.0, 0.6)
|
||||
bright = from_hsv(hue, 1.0, 1.0)
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
v = liquid[x][y]
|
||||
|
||||
# select a colour for this pixel based on how much
|
||||
# "blobfluence" there is at this position in the liquid
|
||||
if v >= 1.5:
|
||||
graphics.set_pen(bright)
|
||||
elif v >= 1.25:
|
||||
graphics.set_pen(mid)
|
||||
elif v >= 1.0:
|
||||
graphics.set_pen(dark)
|
||||
else:
|
||||
graphics.set_pen(0)
|
||||
graphics.pixel(y, x)
|
||||
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
setup_portrait()
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
setup_portrait()
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
update_liquid()
|
||||
move_blobs()
|
||||
draw_portrait()
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail)
|
||||
time.sleep(0.001)
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,131 @@
|
|||
import time
|
||||
import random
|
||||
from stellar import StellarUnicorn, Channel
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_P8
|
||||
|
||||
"""
|
||||
A gloriously terrible melody maker.
|
||||
|
||||
Use Vol + and Vol - to move up/down (note pitch)
|
||||
|
||||
Use Lux - and D to move left/right (note position)
|
||||
|
||||
Press A to set a note.
|
||||
|
||||
Press B to delete a note.
|
||||
|
||||
Use Lux + to play/pause.
|
||||
"""
|
||||
|
||||
NOTE_DURATION = 125
|
||||
|
||||
su = StellarUnicorn()
|
||||
su.set_brightness(0.5)
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_UNICORN, pen_type=PEN_P8)
|
||||
|
||||
boopety_beepety = su.synth_channel(0)
|
||||
boopety_beepety.configure(
|
||||
waveforms=Channel.SQUARE | Channel.SINE,
|
||||
attack=0.1,
|
||||
decay=0.5,
|
||||
sustain=0.0,
|
||||
release=1.0,
|
||||
volume=1.0
|
||||
)
|
||||
|
||||
su.play_synth()
|
||||
|
||||
black = graphics.create_pen(0, 0, 0)
|
||||
note = graphics.create_pen(255, 255, 255)
|
||||
cursor_bg = graphics.create_pen(64, 0, 0)
|
||||
cursor = graphics.create_pen(255, 0, 0)
|
||||
playhead = graphics.create_pen(0, 128, 0)
|
||||
|
||||
cursor_position = [0, 0]
|
||||
|
||||
playhead_position = 0
|
||||
|
||||
width, height = graphics.get_bounds()
|
||||
|
||||
notes = [random.randint(0, height) for _ in range(width)]
|
||||
|
||||
last_note_advance = time.ticks_ms()
|
||||
|
||||
last_action = time.ticks_ms()
|
||||
|
||||
playing = True
|
||||
|
||||
|
||||
def debounce(button, duration=100):
|
||||
global last_action
|
||||
if su.is_pressed(button) and time.ticks_ms() - last_action > duration:
|
||||
last_action = time.ticks_ms()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def note_to_frequency(note_number):
|
||||
return int((2 ** ((note_number - 69.0) / 12)) * 440)
|
||||
|
||||
|
||||
while True:
|
||||
if debounce(StellarUnicorn.SWITCH_D):
|
||||
cursor_position[0] -= 1
|
||||
cursor_position[0] %= width
|
||||
|
||||
if debounce(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
cursor_position[0] += 1
|
||||
cursor_position[0] %= width
|
||||
|
||||
if debounce(StellarUnicorn.SWITCH_VOLUME_DOWN):
|
||||
cursor_position[1] += 1
|
||||
cursor_position[1] %= height
|
||||
|
||||
if debounce(StellarUnicorn.SWITCH_VOLUME_UP):
|
||||
cursor_position[1] -= 1
|
||||
cursor_position[1] %= height
|
||||
|
||||
if debounce(StellarUnicorn.SWITCH_BRIGHTNESS_UP, 500):
|
||||
playing = not playing
|
||||
if not playing:
|
||||
boopety_beepety.trigger_release()
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
notes[cursor_position[0]] = cursor_position[1]
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_B):
|
||||
notes[cursor_position[0]] = None
|
||||
|
||||
if time.ticks_ms() - last_note_advance > NOTE_DURATION:
|
||||
current_note = None
|
||||
if playing:
|
||||
playhead_position += 1
|
||||
playhead_position %= width
|
||||
current_note = notes[playhead_position]
|
||||
if current_note is not None:
|
||||
current_note = height - current_note # Bottom = Low, Top = High
|
||||
current_note += 36 # Shift up the scale a couple of octaves
|
||||
current_freq = note_to_frequency(current_note)
|
||||
boopety_beepety.frequency(current_freq)
|
||||
boopety_beepety.trigger_attack()
|
||||
last_note_advance = time.ticks_ms()
|
||||
|
||||
graphics.set_pen(black)
|
||||
graphics.clear()
|
||||
|
||||
graphics.set_pen(playhead)
|
||||
graphics.line(playhead_position, 0, playhead_position, height)
|
||||
|
||||
graphics.set_pen(cursor_bg)
|
||||
graphics.line(cursor_position[0], 0, cursor_position[0], height)
|
||||
|
||||
graphics.set_pen(note)
|
||||
for x in range(width):
|
||||
y = notes[x]
|
||||
if y is not None:
|
||||
graphics.pixel(x, y)
|
||||
|
||||
graphics.set_pen(cursor)
|
||||
graphics.pixel(*cursor_position)
|
||||
|
||||
su.update(graphics)
|
|
@ -0,0 +1,158 @@
|
|||
import time
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
'''
|
||||
A collection of copies of classic terminal styles including
|
||||
C64, MS-DOS, Spectrum, and more. Images and text are drawn
|
||||
pixel by pixel from a pattern of Os and Xs.
|
||||
You can adjust the brightness with LUX + and -.
|
||||
'''
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
prompt_x = 0
|
||||
prompt_y = 4
|
||||
|
||||
c64 = [
|
||||
" ",
|
||||
" ",
|
||||
" OOOOO OOOOOO OO OOOO ",
|
||||
" OO OO OO OOOO OO OO ",
|
||||
" OO OO OO OO OO OO OO ",
|
||||
" OOOOO OOOO OOOOOO OO OO ",
|
||||
" OOOO OO OO OO OO OO ",
|
||||
" OO OO OO OO OO OO OO ",
|
||||
" OO OO OOOOOO OO OO OOOO ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" OO OO XXXXXXX ",
|
||||
" OO OO XXXXXXX ",
|
||||
" OO OO XXXXXXX ",
|
||||
" OOOO XXXXXXX ",
|
||||
" OO XXXXXXX ",
|
||||
" OO OO XXXXXXX ",
|
||||
" OO OO XXXXXXX ",
|
||||
" XXXXXXX ",
|
||||
" "
|
||||
]
|
||||
FOREGROUND_C64 = (230, 210, 250)
|
||||
BACKGROUND_C64 = (20, 20, 120)
|
||||
|
||||
spectrum = [
|
||||
" ",
|
||||
" ",
|
||||
" O OOOO OOOO OOOOO ",
|
||||
" O O O O O O O ",
|
||||
" O O O O O O O ",
|
||||
" O O O OOOOOO O O ",
|
||||
" O O O O O O O ",
|
||||
" OOOOOO OOOO O O OOOOO ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" O O O O XXXXXXXX ",
|
||||
" O O O O X XXXXXX ",
|
||||
" X XXXXXX ",
|
||||
" X XXXXXX ",
|
||||
" X XXXXXX ",
|
||||
" X XXXXXX ",
|
||||
" X X ",
|
||||
" XXXXXXXX ",
|
||||
" "
|
||||
]
|
||||
FOREGROUND_SPECTRUM = (0, 0, 0)
|
||||
BACKGROUND_SPECTRUM = (180, 150, 150)
|
||||
|
||||
bbc_micro = [
|
||||
" ",
|
||||
" ",
|
||||
" OOOOO OO OOOO OOO ",
|
||||
" O O O O O O O ",
|
||||
" O O O O O O ",
|
||||
" OOOOO O O OOOO O ",
|
||||
" O O OOOOOO O O ",
|
||||
" O O O O O O O ",
|
||||
" OOOOO O O OOOO OOO ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" OOOO O ",
|
||||
" O O O ",
|
||||
" O O ",
|
||||
" O O ",
|
||||
" O O ",
|
||||
" O O O ",
|
||||
" OOOO O ",
|
||||
" XXXXXXX ",
|
||||
" "
|
||||
]
|
||||
FOREGROUND_BBC_MICRO = (255, 255, 255)
|
||||
BACKGROUND_BBC_MICRO = (0, 0, 0)
|
||||
|
||||
PROMPT_C64 = 0
|
||||
PROMPT_SPECTRUM = 1
|
||||
PROMPT_BBC_MICRO = 2
|
||||
prompt = 0
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw(image, fg, bg, time_ms):
|
||||
fg_pen = graphics.create_pen(fg[0], fg[1], fg[2])
|
||||
bg_pen = graphics.create_pen(bg[0], bg[1], bg[2])
|
||||
graphics.set_pen(bg_pen)
|
||||
graphics.clear()
|
||||
for y in range(len(image)):
|
||||
row = image[y]
|
||||
for x in range(len(row)):
|
||||
pixel = row[x]
|
||||
# draw the prompt text
|
||||
if pixel == 'O':
|
||||
graphics.set_pen(fg_pen)
|
||||
|
||||
# draw the caret blinking
|
||||
elif pixel == 'X' and (time_ms // 300) % 2:
|
||||
graphics.set_pen(fg_pen)
|
||||
|
||||
else:
|
||||
graphics.set_pen(bg_pen)
|
||||
|
||||
graphics.pixel(x + prompt_x, y + prompt_y)
|
||||
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
while True:
|
||||
|
||||
time_ms = time.ticks_ms()
|
||||
prompt = (time_ms // 3000) % 3
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
if prompt == PROMPT_C64:
|
||||
draw(c64, FOREGROUND_C64, BACKGROUND_C64, time_ms)
|
||||
|
||||
elif prompt == PROMPT_SPECTRUM:
|
||||
draw(spectrum, FOREGROUND_SPECTRUM, BACKGROUND_SPECTRUM, time_ms)
|
||||
|
||||
elif prompt == PROMPT_BBC_MICRO:
|
||||
draw(bbc_micro, FOREGROUND_BBC_MICRO, BACKGROUND_BBC_MICRO, time_ms)
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail)
|
||||
time.sleep(0.001)
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,115 @@
|
|||
import gc
|
||||
import time
|
||||
import random
|
||||
from stellar import StellarUnicorn, Channel
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_P8
|
||||
from ulab import numpy
|
||||
|
||||
"""
|
||||
A random, computer effect.
|
||||
Experiment with the damping, number of spawns and intensity to change the effect.
|
||||
"""
|
||||
|
||||
# MAXIMUM OVERKILL
|
||||
# machine.freq(250_000_000)
|
||||
|
||||
DAMPING_FACTOR = 0.95
|
||||
NUMBER_OF_LIGHTS = 10
|
||||
INTENSITY = 20
|
||||
|
||||
volume = 0.5
|
||||
|
||||
su = StellarUnicorn()
|
||||
su.set_brightness(0.5)
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_UNICORN, pen_type=PEN_P8)
|
||||
|
||||
boopety_beepety = su.synth_channel(0)
|
||||
boopety_beepety.configure(
|
||||
waveforms=Channel.SQUARE | Channel.SINE,
|
||||
attack=0.1,
|
||||
decay=0.1,
|
||||
sustain=0.0,
|
||||
release=0.5,
|
||||
volume=volume
|
||||
)
|
||||
|
||||
su.play_synth()
|
||||
|
||||
# Fill palette with a yellow
|
||||
r, g, b = (230, 150, 0)
|
||||
PALETTE_ENTRIES = 255
|
||||
for x in range(PALETTE_ENTRIES):
|
||||
_ = graphics.create_pen(r * x // PALETTE_ENTRIES, g * x // PALETTE_ENTRIES, b)
|
||||
|
||||
|
||||
def update():
|
||||
computer[:] *= DAMPING_FACTOR
|
||||
|
||||
# Spawn random drops
|
||||
for _ in range(NUMBER_OF_LIGHTS):
|
||||
x = random.randint(0, width - 1)
|
||||
y = random.randint(0, height - 1)
|
||||
computer[y][x] = random.randint(0, INTENSITY)
|
||||
|
||||
|
||||
def draw():
|
||||
# Copy the effect to the framebuffer
|
||||
memoryview(graphics)[:] = numpy.ndarray(numpy.clip(computer, 0, 1) * (PALETTE_ENTRIES - 1), dtype=numpy.uint8).tobytes()
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
computer = numpy.zeros((height, width))
|
||||
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
|
||||
while True:
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_DOWN):
|
||||
volume -= 0.1
|
||||
volume = max(0.0, volume)
|
||||
boopety_beepety.volume(volume)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_UP):
|
||||
volume += 0.1
|
||||
volume = min(1.0, volume)
|
||||
boopety_beepety.volume(volume)
|
||||
|
||||
tstart = time.ticks_ms()
|
||||
gc.collect()
|
||||
update()
|
||||
draw()
|
||||
tfinish = time.ticks_ms()
|
||||
|
||||
# Play random notes between 100 and 880Hz for a computery effect
|
||||
boopety_beepety.frequency(random.randint(100, 880))
|
||||
boopety_beepety.trigger_attack()
|
||||
|
||||
total = tfinish - tstart
|
||||
t_total += total
|
||||
t_count += 1
|
||||
|
||||
if t_count == 60:
|
||||
per_frame_avg = t_total / t_count
|
||||
print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,126 @@
|
|||
import time
|
||||
import gc
|
||||
import random
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_P8
|
||||
from ulab import numpy
|
||||
|
||||
"""
|
||||
Classic fire effect.
|
||||
Play with the number of spawns, heat, damping factor and colour palette to tweak it.
|
||||
"""
|
||||
|
||||
# MAXIMUM OVERKILL
|
||||
# machine.freq(250_000_000)
|
||||
|
||||
su = StellarUnicorn()
|
||||
su.set_brightness(0.5)
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_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
|
||||
"""
|
||||
# Raging Gas Inferno
|
||||
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(90, 90, 255)
|
||||
graphics.create_pen(255, 0, 255)
|
||||
"""
|
||||
# Original Colours
|
||||
graphics.create_pen(0, 0, 0)
|
||||
graphics.create_pen(20, 20, 20)
|
||||
graphics.create_pen(180, 30, 0)
|
||||
graphics.create_pen(220, 160, 0)
|
||||
graphics.create_pen(255, 255, 180)
|
||||
|
||||
PALETTE_SIZE = 5 # Should match the number of colours defined above
|
||||
|
||||
|
||||
def update():
|
||||
# Clear the bottom two rows (off screen)
|
||||
heat[height - 1][:] = 0.0
|
||||
heat[height - 2][:] = 0.0
|
||||
|
||||
# Add random fire spawns
|
||||
for c in range(FIRE_SPAWNS):
|
||||
x = random.randint(0, width - 4) + 2
|
||||
heat[height - 1][x - 1:x + 1] = HEAT / 2.0
|
||||
heat[height - 2][x - 1:x + 1] = HEAT
|
||||
|
||||
# Propagate the fire upwards
|
||||
a = numpy.roll(heat, -1, axis=0) # y + 1, x
|
||||
b = numpy.roll(heat, -2, axis=0) # y + 2, x
|
||||
c = numpy.roll(heat, -1, axis=0) # y + 1
|
||||
d = numpy.roll(c, 1, axis=1) # y + 1, x + 1
|
||||
e = numpy.roll(c, -1, axis=1) # y + 1, x - 1
|
||||
|
||||
# Average over 5 adjacent pixels and apply damping
|
||||
heat[:] += a + b + d + e
|
||||
heat[:] *= DAMPING_FACTOR / 5.0
|
||||
|
||||
|
||||
def draw():
|
||||
# Copy the fire effect to the framebuffer
|
||||
# Clips the fire to 0.0 to 1.0
|
||||
# Multiplies it by the number of palette entries (-1) to turn it into a palette index
|
||||
# Converts to uint8_t datatype to match the framebuffer
|
||||
memoryview(graphics)[:] = numpy.ndarray(numpy.clip(heat[0:16, 0:16], 0, 1) * (PALETTE_SIZE - 1), dtype=numpy.uint8).tobytes()
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT + 4
|
||||
heat = numpy.zeros((height, width))
|
||||
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
while True:
|
||||
gc.collect()
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
tstart = time.ticks_ms()
|
||||
gc.collect()
|
||||
update()
|
||||
draw()
|
||||
tfinish = time.ticks_ms()
|
||||
|
||||
total = tfinish - tstart
|
||||
t_total += total
|
||||
t_count += 1
|
||||
|
||||
if t_count == 60:
|
||||
per_frame_avg = t_total / t_count
|
||||
print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,118 @@
|
|||
import gc
|
||||
import time
|
||||
import math
|
||||
import random
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_P8
|
||||
from ulab import numpy
|
||||
|
||||
"""
|
||||
A lava lamp effect, created by blurred, moving particles.
|
||||
"""
|
||||
|
||||
# MAXIMUM OVERKILL
|
||||
# machine.freq(250_000_000)
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_UNICORN, pen_type=PEN_P8)
|
||||
su.set_brightness(0.5)
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
lava = numpy.zeros((height, width))
|
||||
|
||||
|
||||
class Blob():
|
||||
def __init__(self):
|
||||
self.x = float(random.randint(0, width - 1))
|
||||
self.y = float(random.randint(0, height - 1))
|
||||
self.r = (float(random.randint(0, 40)) / 10.0) + 5.0
|
||||
self.dx = (float(random.randint(0, 2)) / 20.0) - 0.05
|
||||
self.dy = (float(random.randint(0, 2)) / 20.0) - 0.025 # positive bias
|
||||
|
||||
def move(self):
|
||||
self.x += self.dx
|
||||
self.y += self.dy
|
||||
|
||||
if self.x < 0.0 or self.x >= float(width):
|
||||
self.x = max(0.0, self.x)
|
||||
self.x = min(float(width - 1), self.x)
|
||||
self.dx = -self.dx
|
||||
|
||||
if self.y < 0.0 or self.y >= float(height):
|
||||
self.y = max(0.0, self.y)
|
||||
self.y = min(float(height - 1), self.y)
|
||||
self.dy = -self.dy
|
||||
|
||||
|
||||
blobs = [Blob() for _ in range(10)]
|
||||
|
||||
|
||||
# Fill palette with a steep falloff from bright red to dark blue
|
||||
PAL_COLS = 9
|
||||
for x in range(PAL_COLS):
|
||||
graphics.create_pen_hsv(0.5 + math.log(x + 1, PAL_COLS + 1) / 2.0, 1.0, math.log(x + 1, PAL_COLS + 1))
|
||||
|
||||
|
||||
def update():
|
||||
# Update the blobs and draw them into the effect
|
||||
for blob in blobs:
|
||||
blob.move()
|
||||
lava[int(blob.y)][int(blob.x)] = blob.r
|
||||
|
||||
# Propogate the blobs outwards
|
||||
a = numpy.roll(lava, 1, axis=0)
|
||||
b = numpy.roll(lava, -1, axis=0)
|
||||
d = numpy.roll(lava, 1, axis=1)
|
||||
e = numpy.roll(lava, -1, axis=1)
|
||||
|
||||
# Average over 5 adjacent pixels and apply damping
|
||||
lava[:] += a + b + d + e
|
||||
lava[:] *= 0.97 / 5.0
|
||||
|
||||
|
||||
def draw():
|
||||
# Copy the lava effect to the framebuffer
|
||||
# Clips to 0.0 - 1.0
|
||||
# Multiplies by palette entries (-1) to turn it into a palette index
|
||||
# Converts to uint8_t datatype to match the framebuffer
|
||||
memoryview(graphics)[:] = numpy.ndarray(numpy.clip(lava, 0.0, 1.0) * (PAL_COLS - 1), dtype=numpy.uint8).tobytes()
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
while True:
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
tstart = time.ticks_ms()
|
||||
gc.collect()
|
||||
update()
|
||||
draw()
|
||||
tfinish = time.ticks_ms()
|
||||
|
||||
total = tfinish - tstart
|
||||
t_total += total
|
||||
t_count += 1
|
||||
|
||||
if t_count == 60:
|
||||
per_frame_avg = t_total / t_count
|
||||
print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,145 @@
|
|||
import gc
|
||||
import time
|
||||
import random
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_P8
|
||||
from ulab import numpy
|
||||
|
||||
"""
|
||||
A randomly-seeded game-of-life cellular automata effect.
|
||||
Experiment with the values below to change the effect.
|
||||
|
||||
Press "A" to manually re-seed.
|
||||
"""
|
||||
|
||||
# MAXIMUM OVERKILL
|
||||
# machine.freq(250_000_000)
|
||||
|
||||
INITIAL_LIFE = 500 # Number of live cells to seed
|
||||
GENERATION_TIME_MS = 50 # MS between generations
|
||||
MINIMUM_LIFE = 10 # Auto reseed when only this many alive cells remain
|
||||
SMOOTHED = True # Enable for a more organic if somewhat unsettling feel
|
||||
|
||||
DECAY = 0.90 # Rate at which smoothing effect decays, higher number = more persistent, 1.0 = no decay
|
||||
TENACITY = 32 # Rate at which smoothing effect increases
|
||||
|
||||
su = StellarUnicorn()
|
||||
su.set_brightness(0.5)
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_UNICORN, pen_type=PEN_P8)
|
||||
|
||||
for c in range(256):
|
||||
graphics.create_pen(c // 2, 0, c)
|
||||
|
||||
|
||||
def update():
|
||||
global last_gen
|
||||
|
||||
if SMOOTHED:
|
||||
duration[:] += life * TENACITY
|
||||
duration[:] *= DECAY
|
||||
|
||||
if time.ticks_ms() - last_gen < GENERATION_TIME_MS:
|
||||
return
|
||||
|
||||
last_gen = time.ticks_ms()
|
||||
|
||||
if numpy.sum(life) < MINIMUM_LIFE:
|
||||
seed_life()
|
||||
return
|
||||
|
||||
# Rollin' rollin' rollin.
|
||||
_N = numpy.roll(life, -1, axis=0)
|
||||
_NW = numpy.roll(_N, -1, axis=1)
|
||||
_NE = numpy.roll(_N, 1, axis=1)
|
||||
_S = numpy.roll(life, 1, axis=0)
|
||||
_SW = numpy.roll(_S, -1, axis=1)
|
||||
_SE = numpy.roll(_S, 1, axis=1)
|
||||
_W = numpy.roll(life, -1, axis=1)
|
||||
_E = numpy.roll(life, 1, axis=1)
|
||||
|
||||
# Compute the total neighbours for each cell
|
||||
neighbours[:] = _N + _NW + _NE + _S + _SW + _SE + _W + _E
|
||||
|
||||
next_generation[:] = life[:]
|
||||
|
||||
# Any cells with exactly three neighbours should always stay alive
|
||||
next_generation[:] += neighbours[:] == 3
|
||||
|
||||
# Any alive cells with less than two neighbours should die
|
||||
next_generation[:] -= (neighbours[:] < 2) * life
|
||||
|
||||
# Any alive cells with more than three neighbours should die
|
||||
next_generation[:] -= (neighbours[:] > 3) * life
|
||||
|
||||
life[:] = numpy.clip(next_generation, 0, 1)
|
||||
|
||||
|
||||
def draw():
|
||||
# Copy the effect to the framebuffer
|
||||
if SMOOTHED:
|
||||
memoryview(graphics)[:] = numpy.ndarray(numpy.clip(duration, 0, 255), dtype=numpy.uint8).tobytes()
|
||||
else:
|
||||
memoryview(graphics)[:] = numpy.ndarray(life * 255, dtype=numpy.uint8).tobytes()
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
def seed_life():
|
||||
for _ in range(INITIAL_LIFE):
|
||||
x = random.randint(0, width - 1)
|
||||
y = random.randint(0, height - 1)
|
||||
life[y][x] = int(True) # Avoid: TypeError: 'bool' object isn't iterable
|
||||
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
life = numpy.zeros((height, width), dtype=numpy.bool)
|
||||
next_generation = numpy.zeros((height, width), dtype=numpy.bool)
|
||||
neighbours = numpy.zeros((height, width), dtype=numpy.uint8)
|
||||
duration = numpy.zeros((height, width))
|
||||
last_gen = time.ticks_ms()
|
||||
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
seed_life()
|
||||
|
||||
|
||||
while True:
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
life[:] = int(False)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_B):
|
||||
SMOOTHED = not SMOOTHED
|
||||
|
||||
tstart = time.ticks_ms()
|
||||
gc.collect()
|
||||
update()
|
||||
draw()
|
||||
tfinish = time.ticks_ms()
|
||||
|
||||
total = tfinish - tstart
|
||||
t_total += total
|
||||
t_count += 1
|
||||
|
||||
if t_count == 60:
|
||||
per_frame_avg = t_total / t_count
|
||||
print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,95 @@
|
|||
import gc
|
||||
import time
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_RGB888
|
||||
from ulab import numpy
|
||||
|
||||
"""
|
||||
This example demonstrates how to work with full RGB888 colour in ulab/numpy.
|
||||
|
||||
Each colour channel is given its own array, and these are combined before
|
||||
copying them into the PicoGraphics buffer.
|
||||
|
||||
At great cost to performance (about half the speed) this example works in
|
||||
floating point 0.0 to 1.0 and converts the result to 8bits per channel.
|
||||
"""
|
||||
|
||||
# MAXIMUM OVERKILL
|
||||
# machine.freq(250_000_000)
|
||||
|
||||
su = StellarUnicorn()
|
||||
su.set_brightness(0.5)
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_UNICORN, pen_type=PEN_RGB888)
|
||||
|
||||
|
||||
def update():
|
||||
# Do something basic with the colour channels
|
||||
# to prove this actually works.
|
||||
red[:] = numpy.roll(red, 1, axis=1)
|
||||
green[:] *= 0.999 # Slowly desaturate green
|
||||
blue[:] *= 1.001 # Slowly saturate blue
|
||||
|
||||
|
||||
def draw():
|
||||
# Copy the red, green, blue channels into
|
||||
# their respective places in the RGB_ array
|
||||
rgb[2::4] = red.flatten()
|
||||
rgb[1::4] = green.flatten()
|
||||
rgb[0::4] = blue.flatten()
|
||||
|
||||
# Convert the results to 8bit RGB and copy to the framebuffer
|
||||
memoryview(graphics)[:] = numpy.ndarray(numpy.clip(rgb, 0, 1) * 255, dtype=numpy.uint8).tobytes()
|
||||
|
||||
# Copy the framebuffer to Stellar
|
||||
su.update(graphics)
|
||||
# Whew!
|
||||
|
||||
|
||||
width, height = graphics.get_bounds()
|
||||
|
||||
# Individual channels
|
||||
red = numpy.zeros((height, width))
|
||||
green = numpy.zeros((height, width))
|
||||
blue = numpy.zeros((height, width))
|
||||
|
||||
# Reserved for combined channels
|
||||
rgb = numpy.zeros((width * height * 4),)
|
||||
|
||||
# Stick some gradients in the channels so we have something to look at
|
||||
red[::] = numpy.linspace(0, 1, width)
|
||||
|
||||
# There has to be a better way!?
|
||||
for x in range(width):
|
||||
green[::, x] = numpy.linspace(0, 1, width)
|
||||
blue[::, x] = numpy.linspace(1, 0, width,)
|
||||
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
while True:
|
||||
tstart = time.ticks_ms()
|
||||
gc.collect()
|
||||
update()
|
||||
draw()
|
||||
tfinish = time.ticks_ms()
|
||||
|
||||
total = tfinish - tstart
|
||||
t_total += total
|
||||
t_count += 1
|
||||
|
||||
if t_count == 60:
|
||||
per_frame_avg = t_total / t_count
|
||||
print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,89 @@
|
|||
import gc
|
||||
import time
|
||||
import random
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_P8
|
||||
from ulab import numpy
|
||||
|
||||
"""
|
||||
HELLO NEO.
|
||||
"""
|
||||
|
||||
# MAXIMUM OVERKILL
|
||||
# machine.freq(250_000_000)
|
||||
|
||||
su = StellarUnicorn()
|
||||
su.set_brightness(1.0)
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_UNICORN, pen_type=PEN_P8)
|
||||
|
||||
|
||||
# Fill half the palette with GREEEN
|
||||
for g in range(128):
|
||||
_ = graphics.create_pen(0, g, 0)
|
||||
|
||||
# And half with bright green for white sparkles
|
||||
for g in range(128):
|
||||
_ = graphics.create_pen(128, 128 + g, 128)
|
||||
|
||||
|
||||
def update():
|
||||
trippy[:] *= 0.65
|
||||
|
||||
for _ in range(2):
|
||||
x = random.randint(0, width - 1)
|
||||
y = random.randint(0, height // 2)
|
||||
trippy[y][x] = random.randint(128, 255) / 255.0
|
||||
|
||||
# Propagate downwards
|
||||
old = numpy.ndarray(trippy) * 0.5
|
||||
trippy[:] = numpy.roll(trippy, 1, axis=0)
|
||||
trippy[:] += old
|
||||
|
||||
|
||||
def draw():
|
||||
# Copy the effect to the framebuffer
|
||||
memoryview(graphics)[:] = numpy.ndarray(numpy.clip(trippy, 0, 1) * 254, dtype=numpy.uint8).tobytes()
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
trippy = numpy.zeros((height, width))
|
||||
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
|
||||
while True:
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
tstart = time.ticks_ms()
|
||||
gc.collect()
|
||||
update()
|
||||
draw()
|
||||
tfinish = time.ticks_ms()
|
||||
|
||||
total = tfinish - tstart
|
||||
t_total += total
|
||||
t_count += 1
|
||||
|
||||
if t_count == 60:
|
||||
per_frame_avg = t_total / t_count
|
||||
print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,131 @@
|
|||
import time
|
||||
import gc
|
||||
import random
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_P8
|
||||
from ulab import numpy
|
||||
|
||||
"""
|
||||
THIS IS FINE!
|
||||
"""
|
||||
|
||||
# MAXIMUM OVERKILL
|
||||
# machine.freq(250_000_000)
|
||||
|
||||
su = StellarUnicorn()
|
||||
su.set_brightness(0.5)
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_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
|
||||
"""
|
||||
# Raging Gas Inferno
|
||||
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(90, 90, 255)
|
||||
graphics.create_pen(255, 0, 255)
|
||||
"""
|
||||
# Original Colours
|
||||
graphics.create_pen(0, 0, 0)
|
||||
graphics.create_pen(20, 20, 20)
|
||||
graphics.create_pen(180, 30, 0)
|
||||
graphics.create_pen(220, 160, 0)
|
||||
graphics.create_pen(255, 255, 180)
|
||||
|
||||
PALETTE_SIZE = 5 # Should match the number of colours defined above
|
||||
|
||||
|
||||
def update():
|
||||
# Clear the bottom two rows (off screen)
|
||||
heat[height - 1][:] = 0.0
|
||||
heat[height - 2][:] = 0.0
|
||||
|
||||
# Add random fire spawns
|
||||
for c in range(FIRE_SPAWNS):
|
||||
x = random.randint(0, width - 4) + 2
|
||||
heat[height - 1][x - 1:x + 1] = HEAT / 2.0
|
||||
heat[height - 2][x - 1:x + 1] = HEAT
|
||||
|
||||
# Propagate the fire upwards
|
||||
a = numpy.roll(heat, -1, axis=0) # y + 1, x
|
||||
b = numpy.roll(heat, -2, axis=0) # y + 2, x
|
||||
c = numpy.roll(heat, -1, axis=0) # y + 1
|
||||
d = numpy.roll(c, 1, axis=1) # y + 1, x + 1
|
||||
e = numpy.roll(c, -1, axis=1) # y + 1, x - 1
|
||||
|
||||
# Average over 5 adjacent pixels and apply damping
|
||||
heat[:] += a + b + d + e
|
||||
heat[:] *= DAMPING_FACTOR / 5.0
|
||||
|
||||
|
||||
def draw():
|
||||
# Copy the fire effect to the framebuffer
|
||||
# Clips the fire to 0.0 to 1.0
|
||||
# Multiplies it by the number of palette entries (-1) to turn it into a palette index
|
||||
# Converts to uint8_t datatype to match the framebuffer
|
||||
memoryview(graphics)[:] = numpy.ndarray(numpy.clip(heat[0:16, 0:16], 0, 1) * (PALETTE_SIZE - 1), dtype=numpy.uint8).tobytes()
|
||||
|
||||
# Draw text over the top
|
||||
graphics.set_pen(0)
|
||||
graphics.text("This", 6, 1, 1, 1)
|
||||
graphics.text("is", 11, 9, 1, 1)
|
||||
graphics.text("fine", 6, 17, 1, 1)
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT + 4
|
||||
heat = numpy.zeros((height, width))
|
||||
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
while True:
|
||||
gc.collect()
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
tstart = time.ticks_ms()
|
||||
gc.collect()
|
||||
update()
|
||||
draw()
|
||||
tfinish = time.ticks_ms()
|
||||
|
||||
total = tfinish - tstart
|
||||
t_total += total
|
||||
t_count += 1
|
||||
|
||||
if t_count == 60:
|
||||
per_frame_avg = t_total / t_count
|
||||
print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,97 @@
|
|||
import gc
|
||||
import time
|
||||
import random
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN, PEN_P8
|
||||
from ulab import numpy
|
||||
|
||||
"""
|
||||
A random, trippy effect.
|
||||
Experiment with the damping, number of spawns, intensity and offset to change the effect.
|
||||
"""
|
||||
|
||||
# MAXIMUM OVERKILL
|
||||
# machine.freq(250_000_000)
|
||||
|
||||
su = StellarUnicorn()
|
||||
su.set_brightness(0.5)
|
||||
graphics = PicoGraphics(DISPLAY_STELLAR_UNICORN, pen_type=PEN_P8)
|
||||
|
||||
|
||||
DAMPING_FACTOR = 0.8
|
||||
NUMBER_OF_DROPS = 5
|
||||
INTENSITY = 10
|
||||
OFFSET = 0.0 # Try 0.5
|
||||
|
||||
# Fill palette with a rainbow sweep
|
||||
PALETTE_ENTRIES = 255
|
||||
for x in range(PALETTE_ENTRIES):
|
||||
_ = graphics.create_pen_hsv(float(x) / PALETTE_ENTRIES + OFFSET, 1.0, 1.0)
|
||||
|
||||
|
||||
def update():
|
||||
trippy[:] *= DAMPING_FACTOR
|
||||
|
||||
# Spawn random drops
|
||||
for _ in range(NUMBER_OF_DROPS):
|
||||
x = random.randint(0, width - 1)
|
||||
y = random.randint(0, height - 1)
|
||||
trippy[y][x] = random.randint(0, INTENSITY)
|
||||
|
||||
a = numpy.roll(trippy, 1, axis=0)
|
||||
b = numpy.roll(trippy, -1, axis=0)
|
||||
d = numpy.roll(trippy, 1, axis=1)
|
||||
e = numpy.roll(trippy, -1, axis=1)
|
||||
|
||||
# Average over 5 adjacent pixels and apply damping
|
||||
trippy[:] += a + b + d + e
|
||||
trippy[:] /= 5.0
|
||||
|
||||
|
||||
def draw():
|
||||
# Copy the effect to the framebuffer
|
||||
memoryview(graphics)[:] = numpy.ndarray(numpy.clip(trippy, 0, 1) * (PALETTE_ENTRIES - 1), dtype=numpy.uint8).tobytes()
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
trippy = numpy.zeros((height, width))
|
||||
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
|
||||
while True:
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
tstart = time.ticks_ms()
|
||||
gc.collect()
|
||||
update()
|
||||
draw()
|
||||
tfinish = time.ticks_ms()
|
||||
|
||||
total = tfinish - tstart
|
||||
t_total += total
|
||||
t_count += 1
|
||||
|
||||
if t_count == 60:
|
||||
per_frame_avg = t_total / t_count
|
||||
print(f"60 frames in {t_total}ms, avg {per_frame_avg:.02f}ms per frame, {1000/per_frame_avg:.02f} FPS")
|
||||
t_count = 0
|
||||
t_total = 0
|
||||
|
||||
# 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)
|
|
@ -0,0 +1,115 @@
|
|||
import time
|
||||
import math
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
'''
|
||||
Some good old fashioned rainbows!
|
||||
|
||||
You can adjust the cycling speed with A and B,
|
||||
stripe width with C and D, hue with VOL + and -,
|
||||
and the brightness with LUX + and -.
|
||||
The sleep button stops the animation (can be started again with A or B).
|
||||
'''
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def from_hsv(h, s, v):
|
||||
i = math.floor(h * 6.0)
|
||||
f = h * 6.0 - i
|
||||
v *= 255.0
|
||||
p = v * (1.0 - s)
|
||||
q = v * (1.0 - f * s)
|
||||
t = v * (1.0 - (1.0 - f) * s)
|
||||
|
||||
i = int(i) % 6
|
||||
if i == 0:
|
||||
return int(v), int(t), int(p)
|
||||
if i == 1:
|
||||
return int(q), int(v), int(p)
|
||||
if i == 2:
|
||||
return int(p), int(v), int(t)
|
||||
if i == 3:
|
||||
return int(p), int(q), int(v)
|
||||
if i == 4:
|
||||
return int(t), int(p), int(v)
|
||||
if i == 5:
|
||||
return int(v), int(p), int(q)
|
||||
|
||||
|
||||
@micropython.native # noqa: F821
|
||||
def draw():
|
||||
global hue_offset, phase
|
||||
phase_percent = phase / 15
|
||||
for x in range(width):
|
||||
colour = hue_map[int((x + (hue_offset * width)) % width)]
|
||||
for y in range(height):
|
||||
v = ((math.sin((x + y) / stripe_width + phase_percent) + 1.5) / 2.5)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(int(colour[0] * v), int(colour[1] * v), int(colour[2] * v)))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
hue_map = [from_hsv(x / width, 1.0, 1.0) for x in range(width)]
|
||||
hue_offset = 0.0
|
||||
|
||||
animate = True
|
||||
stripe_width = 3.0
|
||||
speed = 1.0
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
phase = 0
|
||||
while True:
|
||||
|
||||
if animate:
|
||||
phase += speed
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_UP):
|
||||
hue_offset += 0.01
|
||||
hue_offset = 1.0 if hue_offset > 1.0 else hue_offset
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_DOWN):
|
||||
hue_offset -= 0.01
|
||||
hue_offset = 0.0 if hue_offset < 0.0 else hue_offset
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_SLEEP):
|
||||
animate = False
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
speed += 0.05
|
||||
speed = 10.0 if speed > 10.0 else speed
|
||||
animate = True
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_B):
|
||||
speed -= 0.05
|
||||
speed = 0.0 if speed < 0.0 else speed
|
||||
animate = True
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_C):
|
||||
stripe_width += 0.05
|
||||
stripe_width = 10.0 if stripe_width > 10.0 else stripe_width
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_D):
|
||||
stripe_width -= 0.05
|
||||
stripe_width = 1.0 if stripe_width < 1.0 else stripe_width
|
||||
|
||||
start = time.ticks_ms()
|
||||
|
||||
draw()
|
||||
|
||||
print("total took: {} ms".format(time.ticks_ms() - start))
|
|
@ -0,0 +1,96 @@
|
|||
import time
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
'''
|
||||
Display scrolling wisdom, quotes or greetz.
|
||||
|
||||
You can adjust the brightness with LUX + and -.
|
||||
'''
|
||||
|
||||
# constants for controlling scrolling text
|
||||
PADDING = 5
|
||||
MESSAGE_COLOUR = (255, 255, 255)
|
||||
OUTLINE_COLOUR = (0, 0, 0)
|
||||
BACKGROUND_COLOUR = (10, 0, 96)
|
||||
MESSAGE = "\"Space is big. Really big. You just won't believe how vastly hugely mind-bogglingly big it is. I mean, you may think it's a long way down the road to the chemist, but that's just peanuts to space.\" - Douglas Adams"
|
||||
HOLD_TIME = 2.0
|
||||
STEP_TIME = 0.075
|
||||
|
||||
# create stellar object and graphics surface for drawing
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
width = StellarUnicorn.WIDTH
|
||||
height = StellarUnicorn.HEIGHT
|
||||
|
||||
|
||||
# function for drawing outlined text
|
||||
def outline_text(text, x, y):
|
||||
graphics.set_pen(graphics.create_pen(int(OUTLINE_COLOUR[0]), int(OUTLINE_COLOUR[1]), int(OUTLINE_COLOUR[2])))
|
||||
graphics.text(text, x - 1, y - 1, -1, 1)
|
||||
graphics.text(text, x, y - 1, -1, 1)
|
||||
graphics.text(text, x + 1, y - 1, -1, 1)
|
||||
graphics.text(text, x - 1, y, -1, 1)
|
||||
graphics.text(text, x + 1, y, -1, 1)
|
||||
graphics.text(text, x - 1, y + 1, -1, 1)
|
||||
graphics.text(text, x, y + 1, -1, 1)
|
||||
graphics.text(text, x + 1, y + 1, -1, 1)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(int(MESSAGE_COLOUR[0]), int(MESSAGE_COLOUR[1]), int(MESSAGE_COLOUR[2])))
|
||||
graphics.text(text, x, y, -1, 1)
|
||||
|
||||
|
||||
su.set_brightness(0.5)
|
||||
|
||||
# state constants
|
||||
STATE_PRE_SCROLL = 0
|
||||
STATE_SCROLLING = 1
|
||||
STATE_POST_SCROLL = 2
|
||||
|
||||
shift = 0
|
||||
state = STATE_PRE_SCROLL
|
||||
|
||||
# set the font
|
||||
graphics.set_font("bitmap8")
|
||||
|
||||
# calculate the message width so scrolling can happen
|
||||
msg_width = graphics.measure_text(MESSAGE, 1)
|
||||
|
||||
last_time = time.ticks_ms()
|
||||
|
||||
while True:
|
||||
time_ms = time.ticks_ms()
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
if state == STATE_PRE_SCROLL and time_ms - last_time > HOLD_TIME * 1000:
|
||||
if msg_width + PADDING * 2 >= width:
|
||||
state = STATE_SCROLLING
|
||||
last_time = time_ms
|
||||
|
||||
if state == STATE_SCROLLING and time_ms - last_time > STEP_TIME * 1000:
|
||||
shift += 1
|
||||
if shift >= (msg_width + PADDING * 2) - width - 1:
|
||||
state = STATE_POST_SCROLL
|
||||
last_time = time_ms
|
||||
|
||||
if state == STATE_POST_SCROLL and time_ms - last_time > HOLD_TIME * 1000:
|
||||
state = STATE_PRE_SCROLL
|
||||
shift = 0
|
||||
last_time = time_ms
|
||||
|
||||
graphics.set_pen(graphics.create_pen(int(BACKGROUND_COLOUR[0]), int(BACKGROUND_COLOUR[1]), int(BACKGROUND_COLOUR[2])))
|
||||
graphics.clear()
|
||||
|
||||
outline_text(MESSAGE, x=PADDING - shift, y=4)
|
||||
|
||||
# update the display
|
||||
su.update(graphics)
|
||||
|
||||
# pause for a moment (important or the USB serial device will fail)
|
||||
time.sleep(0.001)
|
|
@ -0,0 +1,11 @@
|
|||
# Stellar Paint
|
||||
|
||||
Stellar Paint lets you paint pixels onto your Stellar Unicorn over WiFi, in realtime!
|
||||
|
||||
## Setting Up
|
||||
|
||||
You'll need `WIFI_CONFIG.py` from the `common` directory to be saved to your Pico W. Open up `WIFI_CONFIG.py` in Thonny to add your wifi details (and save it when you're done).
|
||||
|
||||
You will also have to install `micropython-phew` and `microdot` through Thonny's Tools -> Manage Packages.
|
||||
|
||||
Run the example through Thonny and it should get connected and give you a URL to visit. Open that URL in your browser and start painting!
|
|
@ -0,0 +1,113 @@
|
|||
import os
|
||||
from microdot_asyncio import Microdot, send_file
|
||||
from microdot_asyncio_websocket import with_websocket
|
||||
from phew import connect_to_wifi
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
from WIFI_CONFIG import SSID, PSK
|
||||
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
mv_graphics = memoryview(graphics)
|
||||
su.set_brightness(0.5)
|
||||
|
||||
WIDTH, HEIGHT = graphics.get_bounds()
|
||||
|
||||
ip = connect_to_wifi(SSID, PSK)
|
||||
|
||||
print(f"Start painting at: http://{ip}")
|
||||
|
||||
|
||||
server = Microdot()
|
||||
|
||||
|
||||
@server.route("/", methods=["GET"])
|
||||
def route_index(request):
|
||||
return send_file("stellar_paint/index.html")
|
||||
|
||||
|
||||
@server.route("/static/<path:path>", methods=["GET"])
|
||||
def route_static(request, path):
|
||||
return send_file(f"stellar_paint/static/{path}")
|
||||
|
||||
|
||||
def get_pixel(x, y):
|
||||
if x < WIDTH and y < HEIGHT and x >= 0 and y >= 0:
|
||||
o = (y * WIDTH + x) * 4
|
||||
return tuple(mv_graphics[o:o + 3])
|
||||
return None
|
||||
|
||||
|
||||
def flood_fill(x, y, r, g, b):
|
||||
todo = []
|
||||
|
||||
def fill(x, y, c):
|
||||
if get_pixel(x, y) != c:
|
||||
return
|
||||
|
||||
graphics.pixel(x, y)
|
||||
|
||||
up = get_pixel(x, y - 1)
|
||||
dn = get_pixel(x, y + 1)
|
||||
lf = get_pixel(x - 1, y)
|
||||
ri = get_pixel(x + 1, y)
|
||||
|
||||
if up == c:
|
||||
todo.append((x, y - 1))
|
||||
if dn == c:
|
||||
todo.append((x, y + 1))
|
||||
if lf == c:
|
||||
todo.append((x - 1, y))
|
||||
if ri == c:
|
||||
todo.append((x + 1, y))
|
||||
|
||||
c = get_pixel(x, y)
|
||||
|
||||
if c is None:
|
||||
return
|
||||
|
||||
fill(x, y, c)
|
||||
|
||||
while len(todo):
|
||||
x, y = todo.pop(0)
|
||||
fill(x, y, c)
|
||||
|
||||
|
||||
@server.route('/paint')
|
||||
@with_websocket
|
||||
async def echo(request, ws):
|
||||
while True:
|
||||
data = await ws.receive()
|
||||
try:
|
||||
x, y, r, g, b = [int(n) for n in data[0:5]]
|
||||
graphics.set_pen(graphics.create_pen(r, g, b))
|
||||
graphics.pixel(x, y)
|
||||
|
||||
except ValueError:
|
||||
if data == "show":
|
||||
su.update(graphics)
|
||||
|
||||
if data == "fill":
|
||||
data = await ws.receive()
|
||||
x, y, r, g, b = [int(n) for n in data[0:5]]
|
||||
graphics.set_pen(graphics.create_pen(r, g, b))
|
||||
flood_fill(x, y, r, g, b)
|
||||
|
||||
if data == "clear":
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||
graphics.clear()
|
||||
|
||||
if data == "save":
|
||||
filename = await ws.receive()
|
||||
print(f"Saving to {filename}.bin")
|
||||
try:
|
||||
os.mkdir("saves")
|
||||
except OSError:
|
||||
pass
|
||||
with open(f"saves/{filename}.bin", "wb") as f:
|
||||
f.write(graphics)
|
||||
await ws.send(f"alert: Saved to saves/{filename}.bin")
|
||||
|
||||
|
||||
server.run(host="0.0.0.0", port=80)
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Stellar Paint</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="//cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="/static/paint.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="window">
|
||||
<h1>Stellar Paint</h1>
|
||||
<table cellspacing="0" cellpadding="0" border-collapse="collapse">
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<div id="palette">
|
||||
<ul>
|
||||
<li class="selected" style="background:rgb(0,0,0);"></li>
|
||||
<li style="background:rgb(132,0,0);"></li>
|
||||
<li style="background:rgb(0,132,0);"></li>
|
||||
<li style="background:rgb(132,132,0);"></li>
|
||||
<li style="background:rgb(0,0,132);"></li>
|
||||
<li style="background:rgb(132,0,132);"></li>
|
||||
<li style="background:rgb(0,132,132);"></li>
|
||||
<li style="background:rgb(132,132,132);"></li>
|
||||
<li style="background:rgb(198,198,198);"></li>
|
||||
<li style="background:rgb(255,0,0);"></li>
|
||||
<li style="background:rgb(0,255,0);"></li>
|
||||
<li style="background:rgb(255,255,0);"></li>
|
||||
<li style="background:rgb(0,0,255);"></li>
|
||||
<li style="background:rgb(255,0,255);"></li>
|
||||
<li style="background:rgb(0,255,255);"></li>
|
||||
<li style="background:rgb(255,255,255);"></li>
|
||||
</ul>
|
||||
|
||||
<input type="color" id="custom" name="custom" value="#ff0000">
|
||||
</div>
|
||||
<ul class="tools">
|
||||
<li data-tool="paint" class="paint selected"><span class="fa fa-pencil"></span></li>
|
||||
<li data-tool="fill" class="fill"><span class="fa fa-bitbucket"></span></li>
|
||||
<li data-tool="erase" class="erase"><span class="fa fa-eraser"></span></li>
|
||||
<li data-tool="pick" class="pick"><span class="fa fa-eyedropper"></span></li>
|
||||
<li data-tool="lighten" class="lighten"><span class="fa fa-sun-o"></span></li>
|
||||
<li data-tool="darken" class="darken"><span class="fa fa-adjust"></span></li>
|
||||
<li data-tool="trash" class="trash"><span class="fa fa-trash"></span></li>
|
||||
<li data-tool="save" class="save"><span class="fa fa-save"></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<script type="text/javascript" src="//cdn.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/tinycolor.js"></script>
|
||||
<script type="text/javascript" src="/static/paint.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
body {
|
||||
background:#333;
|
||||
padding:20px;
|
||||
font-family:Arial, Verdana, Sans-Serif;
|
||||
background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAaUlEQVQYV33Q0Q3AIAgEUBjBFVyBFRzbWVjBEajXBIOVypcJj1NhETG61BiDVJX4Bh211v5hRDiniV+Elx0wQwd0hEatlUop65srMSah23vf8Auz65AWMc8rDHvCCjAQK2KeDcuQDzh+AHEJX8mbbU1BAAAAAElFTkSuQmCC) repeat;
|
||||
}
|
||||
|
||||
.icons {
|
||||
position:absolute;
|
||||
margin:0;
|
||||
padding:20px;
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
.icons li {
|
||||
margin:20px;
|
||||
padding:0;
|
||||
list-style:none;
|
||||
padding-top:80px;
|
||||
width:100px;
|
||||
}
|
||||
|
||||
.icons li span {
|
||||
background:#FFF;
|
||||
color:#000;
|
||||
border:1px solid #000;
|
||||
line-height:20px;
|
||||
padding:5px 10px;
|
||||
text-align:center;
|
||||
font-size:10px;
|
||||
line-height:10px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
#palette ul, #palette li {
|
||||
margin:0;padding:0;list-style:none;
|
||||
}
|
||||
|
||||
#palette {
|
||||
list-style:none;
|
||||
position:relative;
|
||||
height: 122px;
|
||||
padding:0 8px;
|
||||
}
|
||||
|
||||
#palette ul {
|
||||
display:block;
|
||||
width:456px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#palette li, #palette input {
|
||||
border: 2px outset;
|
||||
width:49px;
|
||||
height:49px;
|
||||
float:left;
|
||||
display:block;
|
||||
margin:2px;
|
||||
}
|
||||
|
||||
#palette input {
|
||||
width:110px;
|
||||
height:110px;
|
||||
}
|
||||
|
||||
.window {
|
||||
width: 640px;
|
||||
position: relative;
|
||||
background: #0E071A;
|
||||
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.tools {
|
||||
margin:0;padding:0;list-style:none;
|
||||
clear:both;
|
||||
display:block;
|
||||
position:absolute;
|
||||
top: 50px;
|
||||
right: 8px;
|
||||
width: 98px;
|
||||
background:#999999;
|
||||
font-size:0;
|
||||
}
|
||||
.tools span {
|
||||
line-height:30px;
|
||||
}
|
||||
|
||||
.tools li {
|
||||
font-size:16px;
|
||||
width: 45px;
|
||||
height: 40px;
|
||||
text-align:center;
|
||||
margin:0;
|
||||
padding:0;
|
||||
display:inline-block;
|
||||
line-height:40px;
|
||||
border:2px outset #EEEEEE;
|
||||
background:#F5F5F5;
|
||||
cursor:pointer;
|
||||
color:#000;
|
||||
}
|
||||
|
||||
.tools li.selected {
|
||||
background:#000;
|
||||
color:#FFF;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #FFF;
|
||||
background: #6D38BB;
|
||||
height:40px;
|
||||
margin:0;
|
||||
padding:0 8px;
|
||||
line-height:40px;
|
||||
font-weight:normal;
|
||||
font-size:24px;
|
||||
}
|
||||
|
||||
table {
|
||||
clear:both;
|
||||
cursor:pointer;
|
||||
margin:10px;
|
||||
border:1px solid #333;
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
table td {
|
||||
width:14px;
|
||||
height:14px;
|
||||
border:1px solid #333;
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
'use strict';
|
||||
|
||||
var WIDTH = 16;
|
||||
var HEIGHT = 16;
|
||||
var md = false;
|
||||
var color = tinycolor('#840000');
|
||||
var update;
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
var picker = $('#custom');
|
||||
var palette = $('#palette');
|
||||
|
||||
picker.val(color.toHexString());
|
||||
|
||||
$(document)
|
||||
.on('mousedown',function(e){md=true;})
|
||||
.on('mouseup',function(e){md=false;});
|
||||
|
||||
$('table').on('dragstart', function(e){
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
for (var y = 0; y < HEIGHT; y++) {
|
||||
var row = $('<tr></tr>');
|
||||
for (var x = 0; x < WIDTH; x++) {
|
||||
row.append('<td></td>');
|
||||
}
|
||||
$('tbody').append(row);
|
||||
}
|
||||
|
||||
$('.tools li').on('click', function(){
|
||||
switch($(this).index()){
|
||||
case 6:
|
||||
clear();
|
||||
break;
|
||||
case 7:
|
||||
save();
|
||||
break;
|
||||
default:
|
||||
$('.tools li').removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
picker.on('change', function(){
|
||||
color = tinycolor($(this).val());
|
||||
})
|
||||
|
||||
palette.find('li').on('click', function(){
|
||||
pick(this);
|
||||
});
|
||||
|
||||
function handle_tool(obj, is_click){
|
||||
switch($('.tools li.selected').index()){
|
||||
case 0: //'paint':
|
||||
paint(obj);
|
||||
break;
|
||||
case 1: // Fill
|
||||
if( is_click ) fill(obj);
|
||||
break;
|
||||
case 2: // Erase
|
||||
update_pixel(obj, tinycolor('#000000'));
|
||||
break;
|
||||
case 3: //'pick':
|
||||
pick(obj);
|
||||
break;
|
||||
case 4: //'lighten':
|
||||
lighten(obj);
|
||||
break;
|
||||
case 5: //'darken':
|
||||
darken(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var fill_target = null;
|
||||
var fill_stack = [];
|
||||
function fill(obj){
|
||||
fill_target = tinycolor($(obj).css('background-color')).toRgbString();
|
||||
|
||||
if( fill_target == color.toRgbString() ){
|
||||
return false;
|
||||
}
|
||||
|
||||
var x = $(obj).index();
|
||||
var y = $(obj).parent().index();
|
||||
|
||||
socket.send("fill");
|
||||
socket.send(new Uint8Array([x, y, color.toRgb().r, color.toRgb().g, color.toRgb().b]));
|
||||
socket.send('show');
|
||||
|
||||
do_fill(obj);
|
||||
|
||||
while(fill_stack.length > 0){
|
||||
var pixel = fill_stack.pop();
|
||||
do_fill(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
function is_target_color(obj){
|
||||
return ( tinycolor($(obj).css('background-color')).toRgbString() == fill_target);
|
||||
}
|
||||
|
||||
function do_fill(obj){
|
||||
var obj = $(obj);
|
||||
|
||||
if( is_target_color(obj) ){
|
||||
|
||||
$(obj).css('background-color', color.toRgbString());
|
||||
|
||||
var r = obj.next('td'); // Right
|
||||
var l = obj.prev('td'); // Left
|
||||
var u = obj.parent().prev('tr').find('td:eq(' + obj.index() + ')'); // Above
|
||||
var d = obj.parent().next('tr').find('td:eq(' + obj.index() + ')'); // Below
|
||||
|
||||
if( r.length && is_target_color(r[0]) ) fill_stack.push(r[0]);
|
||||
if( l.length && is_target_color(l[0]) ) fill_stack.push(l[0]);
|
||||
if( u.length && is_target_color(u[0]) ) fill_stack.push(u[0]);
|
||||
if( d.length && is_target_color(d[0]) ) fill_stack.push(d[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function save(){
|
||||
var filename = prompt('Please enter a filename', 'mypaint');
|
||||
filename = filename.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
||||
socket.send('save');
|
||||
socket.send(filename);
|
||||
}
|
||||
|
||||
function clear(){
|
||||
$('td').css('background-color','rgb(0,0,0)').data('changed',false);
|
||||
socket.send('clear');
|
||||
socket.send('show');
|
||||
}
|
||||
|
||||
function lighten(obj){
|
||||
var c = tinycolor($(obj).css('background-color'));
|
||||
c.lighten(5);
|
||||
update_pixel(obj, c);
|
||||
}
|
||||
|
||||
function darken(obj){
|
||||
var c = tinycolor($(obj).css('background-color'));
|
||||
c.darken(5);
|
||||
update_pixel(obj, c);
|
||||
}
|
||||
|
||||
function pick(obj){
|
||||
color = tinycolor($(obj).css('background-color'));
|
||||
picker.val(color.toHexString());
|
||||
}
|
||||
|
||||
function update_pixel(obj, col){
|
||||
var bgcol = tinycolor($(obj).css('background-color'));
|
||||
|
||||
if(col != bgcol){
|
||||
$(obj)
|
||||
.data('changed', true)
|
||||
.css('background-color', col.toRgbString());
|
||||
}
|
||||
}
|
||||
|
||||
function update_pixels(){
|
||||
var changed = false;
|
||||
|
||||
$('td').each(function( index, obj ){
|
||||
if($(obj).data('changed')){
|
||||
$(obj).data('changed',false);
|
||||
changed = true;
|
||||
|
||||
var x = $(this).index();
|
||||
var y = $(this).parent().index();
|
||||
var col = tinycolor($(obj).css('background-color')).toRgb();
|
||||
|
||||
if(socket) {
|
||||
socket.send(new Uint8Array([x, y, col.r, col.g, col.b]));
|
||||
}
|
||||
}
|
||||
});
|
||||
if(changed){
|
||||
socket.send('show');
|
||||
}
|
||||
}
|
||||
|
||||
function paint(obj){
|
||||
update_pixel(obj, color);
|
||||
}
|
||||
|
||||
$('table td').on('click', function(){
|
||||
handle_tool(this, true);
|
||||
});
|
||||
$('table td').on('mousemove', function(){
|
||||
if(!md) return false;
|
||||
handle_tool(this, false);
|
||||
})
|
||||
|
||||
const socket = new WebSocket('ws://' + window.location.host + '/paint');
|
||||
socket.addEventListener('message', ev => {
|
||||
console.log('<<< ' + ev.data);
|
||||
|
||||
if(ev.data.substring(0, 6) == "alert:") {
|
||||
alert(ev.data.substring(6));
|
||||
}
|
||||
});
|
||||
socket.addEventListener('close', ev => {
|
||||
console.log('<<< closed');
|
||||
});
|
||||
|
||||
socket.addEventListener('open', ev => {
|
||||
clear();
|
||||
update = setInterval(update_pixels, 50);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,128 @@
|
|||
import time
|
||||
import network
|
||||
import ntptime
|
||||
import machine
|
||||
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
|
||||
su = StellarUnicorn()
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
# Default Brightness
|
||||
su.set_brightness(0.4)
|
||||
|
||||
# You will need to create or update the file secrets.py with your network credentials using Thonny
|
||||
# in order for the example to update using the NTP.
|
||||
|
||||
# secrets.py should contain:
|
||||
# WIFI_SSID = ""
|
||||
# WIFI_PASSWORD = ""
|
||||
|
||||
try:
|
||||
from secrets import WIFI_SSID, WIFI_PASSWORD
|
||||
except ImportError:
|
||||
print("Create secrets.py with your WiFi credentials")
|
||||
|
||||
|
||||
WIDTH = StellarUnicorn.WIDTH
|
||||
HEIGHT = StellarUnicorn.HEIGHT
|
||||
|
||||
rtc = machine.RTC()
|
||||
|
||||
DAYS = ["Mon", "Tue", "Wed", "Thur", "Fri", "Sat", "Sun"]
|
||||
|
||||
# Enable the Wireless
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
wlan.active(True)
|
||||
|
||||
|
||||
def network_connect(SSID, PSK):
|
||||
|
||||
# Number of attempts to make before timeout
|
||||
max_wait = 5
|
||||
|
||||
# Sets the Wireless LED pulsing and attempts to connect to your local network.
|
||||
print("connecting...")
|
||||
wlan.config(pm=0xa11140) # Turn WiFi power saving off for some slow APs
|
||||
wlan.connect(SSID, PSK)
|
||||
|
||||
while max_wait > 0:
|
||||
if wlan.status() < 0 or wlan.status() >= 3:
|
||||
break
|
||||
max_wait -= 1
|
||||
print('waiting for connection...')
|
||||
time.sleep(1)
|
||||
|
||||
# Handle connection error. Switches the Warn LED on.
|
||||
if wlan.status() != 3:
|
||||
print("Unable to connect. Attempting connection again")
|
||||
|
||||
|
||||
# Function to sync the Pico RTC using NTP
|
||||
def sync_time():
|
||||
|
||||
try:
|
||||
network_connect(WIFI_SSID, WIFI_PASSWORD)
|
||||
except NameError:
|
||||
print("Create secrets.py with your WiFi credentials")
|
||||
|
||||
if wlan.status() < 0 or wlan.status() >= 3:
|
||||
try:
|
||||
ntptime.settime()
|
||||
except OSError:
|
||||
print("Unable to sync with NTP server. Check network and try again.")
|
||||
|
||||
|
||||
def init():
|
||||
|
||||
sync_time()
|
||||
|
||||
|
||||
def draw():
|
||||
|
||||
# Pens
|
||||
RED = graphics.create_pen(120, 0, 0)
|
||||
WHITE = graphics.create_pen(255, 255, 255)
|
||||
|
||||
current_t = rtc.datetime()
|
||||
|
||||
# Set the pen to Red and clear the screen.
|
||||
graphics.set_pen(WHITE)
|
||||
graphics.clear()
|
||||
|
||||
# Measures the length of the text to help us with centring later.
|
||||
day_length = graphics.measure_text(DAYS[current_t[3]], 1)
|
||||
date_length = graphics.measure_text(str(current_t[2]), 1)
|
||||
|
||||
graphics.set_font("bitmap6")
|
||||
graphics.set_pen(RED)
|
||||
graphics.rectangle(0, 0, WIDTH, 7)
|
||||
graphics.set_pen(WHITE)
|
||||
graphics.text(DAYS[current_t[3]], (WIDTH // 2) - (day_length // 2) - 1, 0, 16, 1)
|
||||
|
||||
graphics.set_pen(RED)
|
||||
graphics.set_font("bitmap6")
|
||||
graphics.text(str(current_t[2]), (WIDTH // 2) - (date_length // 2) + 1, 8, 16, 1)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(0, 0, 0))
|
||||
|
||||
su.update(graphics)
|
||||
|
||||
|
||||
init()
|
||||
|
||||
while 1:
|
||||
|
||||
# Adjust Brightness +/-
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
su.adjust_brightness(+0.01)
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
su.adjust_brightness(-0.01)
|
||||
|
||||
# Connect to the network and sync with the NTP server
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_C):
|
||||
sync_time()
|
||||
|
||||
draw()
|
Po Szerokość: | Wysokość: | Rozmiar: 1.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.5 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.6 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.6 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.0 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.0 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.0 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.2 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.3 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.7 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.8 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 1.7 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.2 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.2 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 2.1 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 81 KiB |
|
@ -0,0 +1,153 @@
|
|||
import time
|
||||
from stellar import StellarUnicorn
|
||||
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
|
||||
import WIFI_CONFIG
|
||||
from network_manager import NetworkManager
|
||||
import uasyncio as asyncio
|
||||
import urequests
|
||||
import jpegdec
|
||||
|
||||
# Set your latitude/longitude here (find yours by right clicking in Google Maps!)
|
||||
LAT = 53.38609085276884
|
||||
LNG = -1.4239983439328177
|
||||
TIMEZONE = "auto" # determines time zone from lat/long
|
||||
|
||||
URL = "http://api.open-meteo.com/v1/forecast?latitude=" + str(LAT) + "&longitude=" + str(LNG) + "¤t_weather=true&timezone=" + TIMEZONE
|
||||
WEATHER_TEXT = ''
|
||||
user_icon = None
|
||||
|
||||
|
||||
def get_data():
|
||||
global WEATHER_TEXT, temperature, weathercode
|
||||
print(f"Requesting URL: {URL}")
|
||||
r = urequests.get(URL)
|
||||
# open the json data
|
||||
j = r.json()
|
||||
print("Data obtained!")
|
||||
print(j)
|
||||
|
||||
# parse relevant data from JSON
|
||||
current = j["current_weather"]
|
||||
temperature = current["temperature"]
|
||||
windspeed = current["windspeed"]
|
||||
winddirection = calculate_bearing(current["winddirection"])
|
||||
weathercode = current["weathercode"]
|
||||
date, now = current["time"].split("T")
|
||||
WEATHER_TEXT = f"Temp: {temperature}°C Wind Speed: {windspeed}kmph Wind Direction: {winddirection} As of: {date}, {now}"
|
||||
print(WEATHER_TEXT)
|
||||
r.close()
|
||||
|
||||
|
||||
def calculate_bearing(d):
|
||||
# calculates a compass direction from the wind direction in degrees
|
||||
dirs = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
|
||||
ix = round(d / (360. / len(dirs)))
|
||||
return dirs[ix % len(dirs)]
|
||||
|
||||
|
||||
def status_handler(mode, status, ip):
|
||||
global MESSAGE
|
||||
print("Network: {}".format(WIFI_CONFIG.SSID))
|
||||
|
||||
|
||||
# create galactic object and graphics surface for drawing
|
||||
su = StellarUnicorn()
|
||||
display = PicoGraphics(DISPLAY)
|
||||
|
||||
WIDTH = StellarUnicorn.WIDTH
|
||||
HEIGHT = StellarUnicorn.HEIGHT
|
||||
|
||||
jpeg = jpegdec.JPEG(display)
|
||||
TEXT_COLOUR = display.create_pen(200, 0, 200)
|
||||
BLACK = display.create_pen(0, 0, 0)
|
||||
WHITE = display.create_pen(255, 255, 255)
|
||||
|
||||
|
||||
def run():
|
||||
# Setup wifi
|
||||
network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler)
|
||||
|
||||
# Connect to Wifi network
|
||||
asyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK))
|
||||
while (not network_manager.isconnected()):
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
su.set_brightness(1)
|
||||
run() # Sets up Wifi connection
|
||||
|
||||
|
||||
def outline_text(text, x, y):
|
||||
display.set_pen(BLACK)
|
||||
display.text(text, x - 1, y - 1, -1, 1)
|
||||
display.text(text, x, y - 1, -1, 1)
|
||||
display.text(text, x + 1, y - 1, -1, 1)
|
||||
display.text(text, x - 1, y, -1, 1)
|
||||
display.text(text, x + 1, y, -1, 1)
|
||||
display.text(text, x - 1, y + 1, -1, 1)
|
||||
display.text(text, x, y + 1, -1, 1)
|
||||
display.text(text, x + 1, y + 1, -1, 1)
|
||||
|
||||
display.set_pen(WHITE)
|
||||
display.text(text, x, y, -1, 1)
|
||||
|
||||
|
||||
def draw_page(cycle):
|
||||
global user_icon
|
||||
text_cycle = cycle % 1000
|
||||
cycle = cycle % 4
|
||||
# Clear the display
|
||||
display.set_pen(15)
|
||||
display.clear()
|
||||
|
||||
# Draw the page header
|
||||
display.set_font("bitmap6")
|
||||
|
||||
if temperature is not None:
|
||||
# Choose an appropriate icon based on the weather code
|
||||
# Weather codes from https://open-meteo.com/en/docs
|
||||
if user_icon is not None:
|
||||
icons = ["icons/snow{0}.jpg".format(cycle + 1), "icons/rain{0}.jpg".format(cycle + 1), "icons/cloud{0}.jpg".format(cycle + 1), "icons/sun{0}.jpg".format(cycle + 1), "icons/storm{0}.jpg".format(cycle + 1)]
|
||||
jpeg.open_file(icons[user_icon])
|
||||
else:
|
||||
if weathercode in [71, 73, 75, 77, 85, 86]: # codes for snow
|
||||
jpeg.open_file("icons/snow{0}.jpg".format(cycle + 1))
|
||||
elif weathercode in [51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 80, 81, 82]: # codes for rain
|
||||
jpeg.open_file("icons/rain{0}.jpg".format(cycle + 1))
|
||||
elif weathercode in [1, 2, 3, 45, 48]: # codes for cloud
|
||||
jpeg.open_file("icons/cloud{0}.jpg".format(cycle + 1))
|
||||
elif weathercode in [0]: # codes for sun
|
||||
jpeg.open_file("icons/sun{0}.jpg".format(cycle + 1))
|
||||
elif weathercode in [95, 96, 99]: # codes for storm
|
||||
jpeg.open_file("icons/storm{0}.jpg".format(cycle + 1))
|
||||
jpeg.decode(0, 0, jpegdec.JPEG_SCALE_FULL)
|
||||
display.set_pen(TEXT_COLOUR)
|
||||
outline_text(WEATHER_TEXT, 16 - text_cycle, 0)
|
||||
|
||||
else:
|
||||
display.set_pen(0)
|
||||
display.set_pen(15)
|
||||
display.text("Unable to display weather! Check your network settings in WIFI_CONFIG.py", 5, 65, WIDTH, 1)
|
||||
|
||||
su.update(display)
|
||||
|
||||
|
||||
while 1:
|
||||
|
||||
get_data()
|
||||
for count in range(500):
|
||||
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_A):
|
||||
user_icon = 0
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_B):
|
||||
user_icon = 1
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_C):
|
||||
user_icon = 2
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_D):
|
||||
user_icon = 3
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
|
||||
user_icon = 4
|
||||
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
|
||||
user_icon = None
|
||||
draw_page(count)
|
||||
time.sleep(0.2)
|