Copied Cosmic MPy examples to Stellar

feature/stellar_unicorn
ZodiusInfuser 2023-05-30 12:42:27 +01:00
rodzic 7aa75e57a4
commit 67152e32e5
58 zmienionych plików z 7163 dodań i 2 usunięć

Wyświetl plik

@ -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"

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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))

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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()

Wyświetl plik

@ -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>

Wyświetl plik

@ -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")

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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))

Wyświetl plik

@ -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))

Wyświetl plik

@ -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)

Wyświetl plik

@ -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))

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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))

Wyświetl plik

@ -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)

Wyświetl plik

@ -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!

Wyświetl plik

@ -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)

Wyświetl plik

@ -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>

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);
});
});

Wyświetl plik

@ -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()

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.1 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.5 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.6 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.6 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.0 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.1 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.0 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.0 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.1 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.1 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.2 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.1 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.3 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.7 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.8 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.7 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.2 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.1 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.2 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.1 KiB

Plik diff jest za duży Load Diff

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 81 KiB

Wyświetl plik

@ -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) + "&current_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)